Let’s start with spring.factories, mainly GatewayAutoConfiguration, Here the main loading CompositeRouteDefinitionLocator, RouteDefinitionRouteLocator, FilteringWebHandler, RoutePredicateHandlerMapping, the Route RefreshListener, CachingrouteLocator, etc. RouteLocator and RouteDefinitionLocator role in the previous already mentioned, FilteringWebHandler, RoutePredicateHandlerMapping both waiting for the call, We know they’re going to load here. We mainly look at RouteRefreshListener and CachingrouteLocator.

RouteRefreshListener

RouteRefreshListener implements the ApplicationListener interface, so it calls the onApplicationEvent method and, under certain conditions, the reset method, in the same way Zuul does. So a dynamic refresh can also be triggered by calling its event.

public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent || event instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) { reset(); } else if (event instanceof ParentHeartbeatEvent) { ParentHeartbeatEvent e = (ParentHeartbeatEvent) event; resetIfNeeded(e.getValue()); } else if (event instanceof HeartbeatEvent) { HeartbeatEvent e = (HeartbeatEvent) event; resetIfNeeded(e.getValue()); }}

If we look at the Reset method, it just posts a RefreshRoutesEvent.

private void reset() {
    this.publisher.publishEvent(new RefreshRoutesEvent(this));
}

CachingRouteLocator

CachingrouteLocator is used to receive RefreshRoutesEvent events. It implements the ApplicationListener<RefreshRoutesEvent> interface. Let’s look at his onApplicationEvent method.

public void onApplicationEvent(RefreshRoutesEvent event) { try { fetch().collect(Collectors.toList()).subscribe(list -> Flux.fromIterable(list) .materialize().collect(Collectors.toList()).subscribe(signals -> { applicationEventPublisher .publishEvent(new RefreshRoutesResultEvent(this)); cache.put(CACHE_KEY, signals); }, throwable -> handleRefreshError(throwable))); } catch (Throwable e) { handleRefreshError(e); }}

He will call the fetch method first, here is actually called CompositeRouteLocator# getRoutes, then call RouteDefinitionRouteLocator# getRoutes.

private Flux<Route> fetch() {
    return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
}

RouteDefinitionRouteLocator#getRoutes

This method can see that it gets a collection of RouteDefinitions and then calls the convertToroute method, in which the RouteDefinition is converted to Route.

@Override public Flux<Route> getRoutes() { Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions() .map(this::convertToRoute); // Other omitted}

RouteDefinitionRouteLocator#convertToRoute

The RouteDefinition is converted to Route, which is used to parse the Predicate and GatewayFilter, as we saw in the previous post, using the factory class. The combinePredicates method handles the predicates, and the getFilters handle the filters.

private Route convertToRoute(RouteDefinition routeDefinition) {
    AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
    List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);

    return Route.async(routeDefinition).asyncPredicate(predicate)
            .replaceFilters(gatewayFilters).build();
}

RouteDefinitionRouteLocator#combinePredicates

In this case, it is the predicate required to convert the RouteDefinition’s predicate to the Route. First, you create a Predicate, and then you combine the rest to form a left and right structure. The structure diagram was described in the previous article. The lookup method is where the Predicate is created from the factory class.

private AsyncPredicate<ServerWebExchange> combinePredicates( RouteDefinition routeDefinition) { List<PredicateDefinition> predicates = routeDefinition.getPredicates(); if (predicates == null || predicates.isEmpty()) { // this is a very rare case, but possible, just match all return AsyncPredicate.from(exchange -> true); } asyncPredicate <ServerWebExchange> Predicate = lookup(RouteDefinition, predicates.get(0)); // Combine the rest for (PredicateDefinition andPredicate: predicates.subList(1, predicates.size())) { AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate); predicate = predicate.and(found); } return predicate; }

RouteDefinitionRouteLocator#lookup

In this method, you first get the routePredicateFactory from the name of the configuration, then assemble the config information, and finally create the Predicate from Factory.ApplyAsync.

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());
    }
    if (logger.isDebugEnabled()) {
        logger.debug("RouteDefinition " + route.getId() + " applying "
                + predicate.getArgs() + " to " + predicate.getName());
    }

    // @formatter:off
    Object config = this.configurationService.with(factory)
            .name(predicate.getName())
            .properties(predicate.getArgs())
            .eventFunction((bound, properties) -> new PredicateArgsEvent(
                    RouteDefinitionRouteLocator.this, route.getId(), properties))
            .bind();
    // @formatter:on

    return factory.applyAsync(config);
}

RouteDefinitionRouteLocator#getFilters

This is where the Filter is processed, and it gets the corresponding Filter through the loadGatewayFilters method.

private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) { List<GatewayFilter> filters = new ArrayList<>(); // TODO: support option to apply defaults after route specific filters? if (! this.gatewayProperties.getDefaultFilters().isEmpty()) { filters.addAll(loadGatewayFilters(DEFAULT_FILTERS, new ArrayList<>(this.gatewayProperties.getDefaultFilters()))); } if (! routeDefinition.getFilters().isEmpty()) { filters.addAll(loadGatewayFilters(routeDefinition.getId(), new ArrayList<>(routeDefinition.getFilters()))); } AnnotationAwareOrderComparator.sort(filters); return filters; }

RouteDefinitionRouteLocator#loadGatewayFilters

This design is similar to the above. It also uses the name of the configuration information, gets the corresponding GatewayFilterFactory, then encapsulates the configuration information, and creates a Filter by Factory.Apply.

List<GatewayFilter> loadGatewayFilters(String id,
        List<FilterDefinition> filterDefinitions) {
    ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
    for (int i = 0; i < filterDefinitions.size(); i++) {
        FilterDefinition definition = filterDefinitions.get(i);
        GatewayFilterFactory factory = this.gatewayFilterFactories
                .get(definition.getName());
        if (factory == null) {
            throw new IllegalArgumentException(
                    "Unable to find GatewayFilterFactory with name "
                            + definition.getName());
        }
        if (logger.isDebugEnabled()) {
            logger.debug("RouteDefinition " + id + " applying filter "
                    + definition.getArgs() + " to " + definition.getName());
        }

        // @formatter:off
        Object configuration = this.configurationService.with(factory)
                .name(definition.getName())
                .properties(definition.getArgs())
                .eventFunction((bound, properties) -> new FilterArgsEvent(
                        // TODO: why explicit cast needed or java compile fails
                        RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
                .bind();
        // @formatter:on

        // some filters require routeId
        // TODO: is there a better place to apply this?
        if (configuration instanceof HasRouteId) {
            HasRouteId hasRouteId = (HasRouteId) configuration;
            hasRouteId.setRouteId(id);
        }

        GatewayFilter gatewayFilter = factory.apply(configuration);
        if (gatewayFilter instanceof Ordered) {
            ordered.add(gatewayFilter);
        }
        else {
            ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
        }
    }

    return ordered;
}

The whole process