In the previous article, we saw that you can configure the following configuration to Filter and forward requests, and that there are multiple filters and Predicate built into the SCG. Through the similar – Path = / login or – StripPrefix = 1 that can match to SCG built-in PathRoutePredicateFactory and StripPrefixGatewayFilterFactory, So how does SCG encapsulate and match our configuration?

GatewayProperties gateway: routes: -id: user-service #//127.0.0.1:8080 # Specifies the destination address to which the route is routed-path =/login,/loginUser filters: -stripprefix =. /loginUser filters: -stripprefix =1# say belowCopy the code

Automatically.

SCG is based on Springboot, which automatically assembs beans needed by reading spring.factories files, The spring.factories file in the spring-Cloud-gateway-server project configures the classes that the SCG needs to auto-assemble.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayHystrixCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\
org.springframework.cloud.gateway.config.SimpleUrlHandlerMappingGlobalCorsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration

org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.gateway.config.GatewayEnvironmentPostProcessor
Copy the code

This article only needs to focus on** GatewayAutoConfiguration****, the core configuration class of SCG. * * from GatewayAutoConfiguration annotations can see @ ConditionalOnProperty ` ` (name = ` ` spring. Cloud. Gateway. “enabled” ` `, ` ` matchIfMissing = ` ` true ` `), by the spring. Cloud. Gateway. Enabled to configure SCG opened and closed, and the default is open

GatewayAutoConfigurationThe primary component initialized in

GatewayProperties

	/** * Read the {@link RouteDefinition} ->>{@link FilterDefinition} ->>{@link PredicateDefinition} and encapsulate * @return */
	@Bean
	public GatewayProperties gatewayProperties() {
		return new GatewayProperties();
	}
Copy the code
public class GatewayProperties {

	/** * Properties prefix. */
	public static final String PREFIX = "spring.cloud.gateway";
	/** * List of Routes. */
	@NotNull
	@Valid
	private List<RouteDefinition> routes = new ArrayList<>();

	/** * List of filters that apply to each route */
	private List<FilterDefinition> defaultFilters = new ArrayList<>();
					..........省略部分代码............
}
Copy the code

RouteDefinition Indicates the route information

public class RouteDefinition {
	
  // Route ID. If set to null, SCG will generate a random route ID
	private String id;
	
	@NotEmpty
	@Valid
  // Configure the assertion information
	private List<PredicateDefinition> predicates = new ArrayList<>();

	@Valid
  // Information about the configured filter
	private List<FilterDefinition> filters = new ArrayList<>();

	@NotNull
  // Destination URI to be forwarded
	private URI uri;
}
Copy the code

PredicateDefinition Specifies the assertion information

public class PredicateDefinition {

	@NotNull
	/ * * * the name of the assertion, and {@ link AbstractRoutePredicateFactory} a subclass of the same name prefix * /
	private String name;

	Key :_genkey_0 value:/login */
	private Map<String, String> args = new LinkedHashMap<>();

	public PredicateDefinition() {
	}

	public PredicateDefinition(String text) {
		int eqIdx = text.indexOf('=');
		if (eqIdx <= 0) {
			throw new ValidationException("Unable to parse PredicateDefinition text '"
					+ text + "'" + ", must be of the form name=value");
		}
		setName(text.substring(0, eqIdx));
		// Split the string to the right of "=" with a ","
		String[] args = tokenizeToStringArray(text.substring(eqIdx + 1), ",");
		Key (_genkey_+ parameter subscript) is generated randomly, and value is the parameter
		for (int i = 0; i < args.length; i++) { this.args.put(NameUtils.generateName(i), args[i]); }}}Copy the code

FilterDefinition Filter information

This is similar to PredicateDefinition. At this point, you have encapsulated the information from the configuration into the corresponding Definition

RouteDefinitionLocator

public interface RouteDefinitionLocator {
  
	// Get all routing information
	Flux<RouteDefinition> getRouteDefinitions();

}
Copy the code

The main implementation class: PropertiesRouteDefinitionLocator, CompositeRouteDefinitionLocator

/ * * * PropertiesRouteDefinitionLocator is {@ link RouteDefinitionLocator} implementation class used to store the read from the configuration file of the routing information * @ param properties GatewayProperties Bean * @return */
	@Bean
	@ConditionalOnMissingBean
	public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
			GatewayProperties properties) {
		return new PropertiesRouteDefinitionLocator(properties);
	}
Copy the code
/** * Assemble the above RouteDefinitionLocator again * @param routeDefinitionLocators * @return */
	@Bean
	@Primary / / is defined as Primary to below when assembling RouteDefinitionRouteLocator the Bean as the injected RouteDefinitionLocator Beanpublic RouteDefinitionLocator routeDefinitionLocator( List<RouteDefinitionLocator> routeDefinitionLocators) { return new  CompositeRouteDefinitionLocator( Flux.fromIterable(routeDefinitionLocators)); }Copy the code

This class determines whether the route ID is empty and generates one if it is

public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {

	@Override
	public Flux<RouteDefinition> getRouteDefinitions() {
		return this.delegates
				.flatMapSequential(RouteDefinitionLocator::getRouteDefinitions)
				.flatMap(routeDefinition -> {
					if (routeDefinition.getId() == null) {
						// If the route ID is empty, one is generated
						return randomId().map(id -> {
							routeDefinition.setId(id);
							if (log.isDebugEnabled()) {
								log.debug(
										"Id set on route definition: "+ routeDefinition); } return routeDefinition; }); } return Mono.just(routeDefinition); }); } protected Mono<String> randomId() { return Mono.fromSupplier(idGenerator::toString) .publishOn(Schedulers.boundedElastic()); }}Copy the code

GatewayFilter Factory beans

* * assembly org. Springframework. Cloud. Gateway. Handler. The predicate package under RoutePredicateFactory * * implementation class

@Bean @ConditionalOnEnabledFilter public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() { return new AddRequestHeaderGatewayFilterFactory(); } @Bean @ConditionalOnEnabledFilter public MapRequestHeaderGatewayFilterFactory mapRequestHeaderGatewayFilterFactory() {  return new MapRequestHeaderGatewayFilterFactory(); }... And so on...Copy the code

Predicate Factory beans

Assembly * * org. Springframework. Cloud. Gateway. Filter. The factory under package * * GatewayFilterFactory implementation class

@Bean @ConditionalOnEnabledPredicate public AfterRoutePredicateFactory afterRoutePredicateFactory() { return new AfterRoutePredicateFactory(); } @Bean @ConditionalOnEnabledPredicate public BeforeRoutePredicateFactory beforeRoutePredicateFactory() { return new BeforeRoutePredicateFactory(); }... And so on...Copy the code

RouteLocator

public interface RouteLocator {
	// To get the route
	Flux<Route> getRoutes();

}
Copy the code



RouteDefinitionRouteLocatorThe other two implementations are the focus of this section and will be explained later

	/** ** @param properties is the assembly GatewayProperties Bean * @param gatewayFilters are the assembly GatewayFilterFactory implementation classes * @param Predicates are all implementation classes of the assembled RouteDefinitionFactory. * @param routeDefinitionLocator is the assembled routeDefinitionLocator ->CompositeRouteDefinitionLocator * @return */
	@Bean
	public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
			List<GatewayFilterFactory> gatewayFilters,
			List<RoutePredicateFactory> predicates,
			RouteDefinitionLocator routeDefinitionLocator,
			ConfigurationService configurationService) {
		return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
				gatewayFilters, properties, configurationService);
	}

/ * * * CachingRouteLocator RoutePredicateHandlerMapping RouteLocator, see {@ link this# RoutePredicateHandlerMapping} * @ param Above routeLocators assembly RouteDefinitionRouteLocator Bean * @ return * /
	@Bean
	@Primary / / is defined as the Primary for the bottom assembly RoutePredicateHandlerMapping
	@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
	public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
		return new CachingRouteLocator(
				new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
	}
Copy the code

RouteDefinitionRouteLocator

This class consists of RouteDefinitionLocator, RoutePredicateFactory, and GatewayFilterFactory, which can be converted to a Route.

public class RouteDefinitionRouteLocator
		implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {

	/** * Default filters name. */
	public static final String DEFAULT_FILTERS = "defaultFilters";

	protected final Log logger = LogFactory.getLog(getClass());

	private final RouteDefinitionLocator routeDefinitionLocator;

	private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();

	private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();

	private final GatewayProperties gatewayProperties;

	public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
			List<RoutePredicateFactory> predicates,
			List<GatewayFilterFactory> gatewayFilterFactories,
			GatewayProperties gatewayProperties,
			ConfigurationService configurationService) {
		this.routeDefinitionLocator = routeDefinitionLocator;
		this.configurationService = configurationService;
		// Initializes Predicate information (all of it)
		initFactories(predicates);
		// Initializes Filter information (all), similar to initializing Predicate Predicate information
		gatewayFilterFactories.forEach(
				factory -> this.gatewayFilterFactories.put(factory.name(), factory));
		this.gatewayProperties = gatewayProperties;
	}

	private void initFactories(List<RoutePredicateFactory> predicates) {
		predicates.forEach(factory -> {
			/ / the key for RoutePredicateFactory implementation class name prefix such as AfterRoutePredicateFactory is key for After
			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.");
			}
			// If the assertion Factory already exists, override it, that is, the one built into the SCG
			this.predicates.put(key, factory);
			if (logger.isInfoEnabled()) {
				logger.info("Loaded RoutePredicateFactory [" + key + "]"); }}); }Copy the code

getRoutes()

This method is called by CachingRouteLocator and returns routing information.

Cachin Group Locator will be covered in a later article

	@Override
	public Flux<Route> getRoutes() {
		// Obtain Route by RouteDefinitionsFlux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions() .map(this::convertToRoute); if (! gatewayProperties.isFailOnRouteDefinitionError()) {// instead of letting error bubble up, continue
			routes = routes.onErrorContinue((error, obj) -> {
				if (logger.isWarnEnabled()) {
					logger.warn("RouteDefinition id " + ((RouteDefinition) obj).getId()
							+ " will be ignored. Definition has invalid configs, "+ error.getMessage()); }}); } return routes.map(route -> { if (logger.isDebugEnabled()) { logger.debug("RouteDefinition matched: " + route.getId());
			}
			return route;
		});
	}
Copy the code

ConvertToRoute converts RouteDefinition to Route

RouteDefinition = Route * @param RouteDefinition * @return */
	private Route convertToRoute(RouteDefinition routeDefinition) {
		/** * key * Gets the assertion of RouteDefinition */
		AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
		/** * key * Get the Filter of RouteDefinition */
		List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);

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

combinePredicates

Merge PredicateDefinition into one AsyncPredicate

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));
		// This is done to connect multiple AsyncPredicate classes with (and) if the RouteDefinition is configured with multiple Predicate classes
		for (PredicateDefinition andPredicate : predicates.subList(1,
				predicates.size())) {
			AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
					andPredicate);
			predicate = predicate.and(found);
		}

		return predicate;
	}

private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route,
			PredicateDefinition predicate) {
		// Find the RoutePredicateFactory corresponding to the PredicateDefinition
		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());
		}
		// Every RoutePredicateFactory implementation has a Config, which can be interpreted as the parameter rules we configure
		// @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
		// Generate asynchronous assertions
		return factory.applyAsync(config);
	}
Copy the code

getFilters

Get all filters, sorted

private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
		List<GatewayFilter> filters = new ArrayList<>();
		// Add a default filterif (! this.gatewayProperties.getDefaultFilters().isEmpty()) { filters.addAll(loadGatewayFilters(DEFAULT_FILTERS, new ArrayList<>(this.gatewayProperties.getDefaultFilters()))); }// Add a configured filterif (! routeDefinition.getFilters().isEmpty()) { filters.addAll(loadGatewayFilters(routeDefinition.getId(), new ArrayList<>(routeDefinition.getFilters()))); }/ / sorting
		AnnotationAwareOrderComparator.sort(filters);
		return filters;
	}

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);
			// Get the GatewayFilterFactory of FilterDefinition
			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());
			}
			// Generates a configuration class, similar to Predicate
			// @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);
			}
			/ / generated GatewayFilter
			GatewayFilter gatewayFilter = factory.apply(configuration);
			if (gatewayFilter instanceof Ordered) {
				ordered.add(gatewayFilter);
			}
			else {
				ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
			}
		}

		return ordered;
	}
Copy the code

conclusion

At this point, the main character Route finally appeared. Now that you understand how the SCG generates routes and assertions and filters for routes through configuration, you can analyze the incoming and forwarding of requests.