How Spring Cloud Gateway Initializes and Resolves Route Predicates
This article explains how Spring Cloud Gateway (Hoxton.SR11) initializes predicate factories, binds their Config objects from configuration, creates Route objects, caches them with CachingRouteLocator, and finally matches incoming requests by evaluating combined asynchronous predicates to locate the appropriate route.
Environment: Spring Cloud Gateway Hoxton.SR11
This section explains how predicates and their configuration routing information are initialized and associated to generate route objects, and how each predicate factory's Config object is parsed from configuration.
All Predicate Factories in Spring Cloud Gateway
Naming rule: XxxRoutePredicateFactory. All these factories share the inheritance hierarchy shown below.
public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config> { /* ... */ }
public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> { /* ... */ }
// ... other factoriesAll predicate factories inherit from AbstractRoutePredicateFactory, whose generic type is an internal Config class. The Config values are bound from configuration.
6.1 Gateway Auto‑Configuration
The following class configures all predicates and filters.
public class GatewayAutoConfiguration {
@Bean
@ConditionalOnEnabledPredicate
public PathRoutePredicateFactory pathRoutePredicateFactory() {
return new PathRoutePredicateFactory();
}
@Bean
@ConditionalOnEnabledPredicate
public QueryRoutePredicateFactory queryRoutePredicateFactory() {
return new QueryRoutePredicateFactory();
}
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
}The configuration creates a CachingRouteLocator (the primary RouteLocator) that merges the RouteDefinitionRouteLocator and caches all route information.
public class CachingRouteLocator {
private final RouteLocator delegate;
private final Flux<Route> routes;
private final Map<String, List> cache = new ConcurrentHashMap<>();
private ApplicationEventPublisher applicationEventPublisher;
public CachingRouteLocator(RouteLocator delegate) {
this.delegate = delegate;
routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class)
.onCacheMissResume(this::fetch);
}
private Flux<Route> fetch() {
return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
}
}Instantiating CachingRouteLocator triggers the lookup of all configured Route objects, which are ultimately delegated to RouteDefinitionRouteLocator.
RouteDefinitionRouteLocator’s initFactories method maps each XxxRoutePredicateFactory.
private void initFactories(List<RoutePredicateFactory> predicates) {
predicates.forEach(factory -> {
String key = factory.name();
if (this.predicates.containsKey(key)) {
this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");
}
this.predicates.put(key, factory);
});
}The factory.name() method resolves the predicate name:
default String name() {
return NameUtils.normalizeRoutePredicateName(getClass());
}6.2 Generating Route Objects and Config Binding
The flow is getRoutes → convertToRoute → combinePredicates → lookup.
combinePredicates merges all predicates defined for a route using logical AND.
private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
// other code
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
predicate = predicate.and(found);
}
return predicate;
}The lookup method binds the Config object of a predicate factory with the name and arguments defined in the YAML configuration.
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
}
// Bind configuration properties to the factory’s Config instance
Object config = this.configurationService.with(factory)
.name(predicate.getName())
.properties(predicate.getArgs())
.eventFunction((bound, properties) -> new PredicateArgsEvent(
RouteDefinitionRouteLocator.this, route.getId(), properties))
.bind();
// Apply the predicate factory to obtain an asynchronous predicate
return factory.applyAsync(config);
}RoutePredicateFactory defines a default applyAsync method that converts a synchronous Predicate to an AsyncPredicate.
public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
default AsyncPredicate<ServerWebExchange> applyAsync(C config) {
return toAsyncPredicate(apply(config));
}
}Specific factories (e.g., PathRoutePredicateFactory) implement the apply method to return a GatewayPredicate.
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
// predicate logic
}
};
}The toAsyncPredicate helper wraps the Predicate into an AsyncPredicate.
public static AsyncPredicate<ServerWebExchange> toAsyncPredicate(Predicate<? super ServerWebExchange> predicate) {
Assert.notNull(predicate, "predicate must not be null");
return AsyncPredicate.from(predicate);
}The resulting Route object contains id, uri, order, the combined AsyncPredicate, filters, and metadata, and is stored in CachingRouteLocator.routes.
public class Route implements Ordered {
private final String id;
private final URI uri;
private final int order;
private final AsyncPredicate<ServerWebExchange> predicate;
private final List<GatewayFilter> gatewayFilters;
private final Map<String, Object> metadata;
}6.3 Locating a Route
RouteLocator is used to locate the matching route for an incoming request.
public interface RouteLocator {
Flux<Route> getRoutes();
}When a request arrives, RoutePredicateHandlerMapping iterates over the cached routes, applying each route’s AsyncPredicate until a match is found.
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
.concatMap(route -> Mono.just(route).filterWhen(r -> {
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return r.getPredicate().apply(exchange);
}))
.next()
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange);
return route;
});
}The predicate’s apply method ultimately invokes the GatewayPredicate.test method, which determines whether the current request satisfies the route’s conditions.
Overall process:
The system initializes all predicates and filters.
Configured routing information is packaged into Route objects.
Incoming request paths are matched against these routes to locate the appropriate handler.
Done!
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
