How Spring Cloud Gateway Initializes Predicates and Routes Internally
This article explores how Spring Cloud Gateway (Hoxton.SR11) initializes predicate factories, binds configuration values, creates Route objects, and uses CachingRouteLocator and RouteDefinitionRouteLocator to locate and match routes at startup, detailing the underlying code flow and key classes involved.
Environment
Spring Cloud Gateway version Hoxton.SR11.
Predicate factories and configuration
The section explains how predicates and their configuration are initialized and linked to generate route objects. Each predicate factory has a Config class that receives values from the configuration.
All predicate factories follow the naming convention XxxRoutePredicateFactory and inherit from AbstractRoutePredicateFactory<Config>.
public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config></code>
<code>public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config>An illustration of the factories is shown below:
1.1 Gateway auto‑configuration
The GatewayAutoConfiguration class declares beans for all predicates, filters, and the route locator.
public class GatewayAutoConfiguration {</code>
<code> @Bean @ConditionalOnEnabledPredicate public PathRoutePredicateFactory pathRoutePredicateFactory() { return new PathRoutePredicateFactory(); }</code>
<code> @Bean @ConditionalOnEnabledPredicate public QueryRoutePredicateFactory queryRoutePredicateFactory() { return new QueryRoutePredicateFactory(); }</code>
<code> @Bean public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {</code>
<code> return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties, configurationService);</code>
<code> }</code>
<code> @Bean @Primary @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator") public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {</code>
<code> return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));</code>
<code> }</code>
<code>}During startup the configuration is delegated to RouteDefinitionRouteLocator, while CachingRouteLocator caches all route information.
public class CachingRouteLocator {</code>
<code> private final RouteLocator delegate;</code>
<code> private final Flux<Route> routes;</code>
<code> private final Map<String, List> cache = new ConcurrentHashMap<>();</code>
<code> private ApplicationEventPublisher applicationEventPublisher;</code>
<code> public CachingRouteLocator(RouteLocator delegate) {</code>
<code> this.delegate = delegate;</code>
<code> routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class).onCacheMissResume(this::fetch);</code>
<code> }</code>
<code> private Flux<Route> fetch() { return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE); }</code>
<code>}Initializing predicate factories
The initFactories method of RouteDefinitionRouteLocator registers each XxxRoutePredicateFactory by name.
private void initFactories(List<RoutePredicateFactory> predicates) {</code>
<code> predicates.forEach(factory -> {</code>
<code> String key = factory.name();</code>
<code> if (this.predicates.containsKey(key)) {</code>
<code> this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");</code>
<code> }</code>
<code> this.predicates.put(key, factory);</code>
<code> });</code>
<code>}The default name() implementation normalizes the class name:
default String name() { return NameUtils.normalizeRoutePredicateName(getClass()); }1.2 Generating Route objects and Config binding
The route creation flow is
getRoutes → convertToRoute → combinePredicates → lookup. All routes are built when the application starts.
public Flux<Route> getRoutes() {</code>
<code> Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute);</code>
<code> routes = routes.onErrorContinue((error, obj) -> routes.map(route -> route));</code>
<code>}Combining predicates uses a chain of and operations:
private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {</code>
<code> for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {</code>
<code> AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);</code>
<code> predicate = predicate.and(found);</code>
<code> }</code>
<code> return predicate;</code>
<code>}The lookup method binds the YAML‑defined predicate name, arguments, and the factory’s Config:
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {</code>
<code> RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());</code>
<code> if (factory == null) { throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName()); }</code>
<code> Object config = this.configurationService.with(factory)</code>
<code> .name(predicate.getName())</code>
<code> .properties(predicate.getArgs())</code>
<code> .eventFunction((bound, properties) -> new PredicateArgsEvent(RouteDefinitionRouteLocator.this, route.getId(), properties))</code>
<code> .bind();</code>
<code> return factory.applyAsync(config);</code>
<code>}The applyAsync method converts the synchronous predicate returned by apply(config) into an asynchronous one:
@FunctionalInterface
public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
default AsyncPredicate<ServerWebExchange> applyAsync(C config) {
return toAsyncPredicate(apply(config));
}
}</code>
<code>public Predicate<ServerWebExchange> apply(Config config) { return new GatewayPredicate() { public boolean test() { /* implementation */ } }; }</code>
<code>public static AsyncPredicate<ServerWebExchange> toAsyncPredicate(Predicate<? super ServerWebExchange> predicate) { Assert.notNull(predicate, "predicate must not be null"); return AsyncPredicate.from(predicate); }</code>
<code>static AsyncPredicate<ServerWebExchange> from(Predicate<? super ServerWebExchange> predicate) { return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate)); }Route class definition
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;
}Instances of Route are stored in CachingRouteLocator.routes.
6.3 Locating routes
The RouteLocator interface provides Flux<Route> getRoutes(). When a request arrives, RoutePredicateHandlerMapping (similar to Spring MVC’s HandlerMapping) uses the locator to find a matching route.
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {</code>
<code> return this.routeLocator.getRoutes()</code>
<code> .concatMap(route -> Mono.just(route).filterWhen(r -> {</code>
<code> exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());</code>
<code> return r.getPredicate().apply(exchange);</code>
<code> }).doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e)).onErrorResume(e -> Mono.empty()))</code>
<code> .next()</code>
<code> .map(route -> { if (logger.isDebugEnabled()) { logger.debug("Route matched: " + route.getId()); } validateRoute(route, exchange); return route; });</code>
<code>}The predicate’s apply ultimately invokes GatewayPredicate.test to decide if the current request matches the route.
Overall process
The system initializes all predicate factories and filter factories.
Configured predicate and filter information is packaged into Route objects.
When a request arrives, the router locates the matching Route based on the request path.
Done!
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
