Create a routing handler like this:
Handlers.routing()
.add(Methods.GET, "/*", exchange -> "domain1".equals(exchange.getHostName()), handler1)
.add(Methods.GET, "/{value1}/{value2}", exchange -> "domain2".equals(exchange.getHostName()), handler2);
The handler2 will never be used. Even if the request is on domain2, and the URI matches }}{{"/{value1}/{value2}", it will still only try the "/*" template, and then use the fallback handler when the predicate doesn't match.
It appears that this is because PathTemplateMatcher::trimBase() returns "/" (with a trailing slash) for the "/*"{{ template, whereas it returns "" (without a trailing slash) for the }}"/{value1}/{value2}" template, and the longest matching prefix{{ is used}}{{ (and "/" will always win over "").}}
This will happen even if I set the predicate for "/*" to always return false:
Handlers.routing()
.add(Methods.GET, "/*", exchange -> false, handler1)
.add(Methods.GET, "/{value1}/{value2}", exchange -> true, handler2);