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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How Spring Cloud Gateway Initializes and Resolves Route Predicates

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 factories

All 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!

backendJavamicroservicesRoute Predicatespring-cloud-gateway
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.