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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How Spring Cloud Gateway Initializes Predicates and Routes Internally

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!

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMicroservicesSpring BootSpring Cloud GatewayBackend Routing
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.