摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud-Gateway/route-locator-route-definition/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文主要基于 Spring-Cloud-Gateway 2.0.X M4
阅读源码最好的方式,是使用 IDEA 进行调试 Spring Cloud Gateway 源码,不然会一脸懵逼。
1. 概述
本文主要分享 RouteDefinitionRouteLocator 的源码实现。

蓝色部分 :RouteDefinitionRouteLocator 。
推荐 Spring Cloud 书籍:
请支持正版。下载盗版,等于主动编写低级 BUG 。
程序猿DD —— 《Spring Cloud微服务实战》
两书齐买,京东包邮。
2. RouteDefinitionRouteLocator
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator ,基于 RouteDefinitionLocator 的 RouteLocator 实现类。
RouteDefinitionRouteLocator 从 RouteDefinitionLocator 获取 RouteDefinition ,转换成 Route 。如下图 :

2.1 构造方法
RouteDefinitionRouteLocator 构造方法,代码如下 :
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware {
protected final Log logger = LogFactory.getLog(getClass());
private final RouteDefinitionLocator routeDefinitionLocator;
/**
* RoutePredicateFactory 映射
* key :{@link RoutePredicateFactory#name()}
*/
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
/**
* GatewayFilterFactory 映射
* key :{@link GatewayFilterFactory#name()}
*/
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
private final GatewayProperties gatewayProperties;
private final SpelExpressionParser parser = new SpelExpressionParser();
private BeanFactory beanFactory;
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties) {
// 设置 RouteDefinitionLocator
this.routeDefinitionLocator = routeDefinitionLocator;
// 初始化 RoutePredicateFactory
initFactories(predicates);
// 初始化 RoutePredicateFactory
gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));
// 设置 GatewayProperties
this.gatewayProperties = gatewayProperties;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}routeDefinitionLocator属性,提供 RouteDefinition 的 RouteDefinitionLocator 。predicates属性,RoutePredicateFactory Bean 对象映射。key为{@link RoutePredicateFactory#name()}。通过它,将
RouteDefinition.predicates转换成Route.predicates。第 26 行 :调用
#initFactories()方法,初始化映射。逻辑比较简单,点击 链接 查看代码。
gatewayFilterFactories属性,RoutePredicateFactory Bean 对象映射。key为{@link GatewayFilterFactory#name()}。通过它,将
RouteDefinition.filters转换成Route.filters。第 28 行 :初始化映射。
gatewayProperties属性,使用GatewayProperties.defaultFilters默认过滤器定义数组,添加到每个 Route 。下文会看到相关代码的实现。parser属性,Spring EL 表达式解析器。在 「2.4 获得 Tuple」 会看到它的使用。beanFactory属性,Bean 工厂。
2.2 获得 Route
#getRoutes() 方法,获得 Route 数组。代码如下 :
@Override
public Flux<Route> getRoutes() {
return this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute) // RouteDefinition => Route
//TODO: error handling
.map(route -> { // 打印日志
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}第 3 行 : 调用
RouteDefinitionLocator#getRouteDefinitions()方法,获得 RouteDefinitions 数组。第 4 行 :调用
#convertToRoute()方法,将每个 RouteDefinition 转换成 Route 。该方法在 「2.3 转换 Route」 详细解析。第 7 至 11 行 :打印输出每个 Route 。
2.3 转换 Route
#convertToRoute() 方法,将每个 RouteDefinition 转换成 Route 。代码如下 :
private Route convertToRoute(RouteDefinition routeDefinition) {
// 合并 Predicate
Predicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
// 获得 GatewayFilter
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
// 构建 Route
return Route.builder(routeDefinition)
.predicate(predicate)
.gatewayFilters(gatewayFilters)
.build();
}第 3 行 :调用
#combinePredicates()方法,将RouteDefinition.predicates数组合并成一个java.util.function.Predicate,这样 RoutePredicateHandlerMapping 为请求匹配 Route ,只要调用一次Predicate#test(ServerWebExchange)方法即可。第 5 行 :调用
#getFilters()方法,获得 GatewayFilter 数组。第 7 至 10 行 :构建 Route 。
#combinePredicates() 方法,代码如下 :
private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
// 寻找 Predicate
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));
// 拼接 Predicate
for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
predicate = predicate.and(found);
}
// 返回 Predicate
return predicate;
}
private Predicate<ServerWebExchange> lookup(RouteDefinition routeDefinition, PredicateDefinition predicate) {
// 获得 RoutePredicateFactory
RoutePredicateFactory found = this.predicates.get(predicate.getName());
if (found == null) {
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
}
// 获得 Tuple
Map<String, String> args = predicate.getArgs();
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + routeDefinition.getId() + " applying "
+ args + " to " + predicate.getName());
}
Tuple tuple = getTuple(found, args, this.parser, this.beanFactory);
// 获得 Predicate
return found.apply(tuple);
}第 2 至 9 行 :通过调用
#lookup()方法,查找 PredicateDefinition 对应的 Predicate 。为什么拆成两部分?第一部分找到java.util.function.Predicate,第二部分通过Predicate#and(Predicate)方法不断拼接。第 11 行 :返回 Predicate 。
---------------------------- 分割线 --------------------------
第 14 至 29 行 :
#lookup()方法。第 16 至 19 行 :获得 RoutePredicateFactory Bean 对象。
第 21 至 26 行 :调用
#getTuple()方法,获得 Tuple 。该方法比较复杂,在 「2.4 获得 Tuple」 详细解析。第 28 行 :调用
RoutePredicateFactory#apply(Tuple)方法,创建 Predicate 。在 《Spring-Cloud-Gateway 源码解析 —— 处理器 (3.1) 之 RoutePredicateFactory 路由谓语工厂 》 详细解析。
#getFilters() 方法,代码如下 :
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("defaultFilters",
this.gatewayProperties.getDefaultFilters()));
}
// 添加 配置的过滤器
if (!routeDefinition.getFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
}
// 排序
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
List<GatewayFilter> filters = filterDefinitions.stream()
.map(definition -> { // FilterDefinition => GatewayFilter
// 获得 GatewayFilterFactory
GatewayFilterFactory filter = this.gatewayFilterFactories.get(definition.getName());
if (filter == null) {
throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
}
// 获得 Tuple
Map<String, String> args = definition.getArgs();
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
}
Tuple tuple = getTuple(filter, args, this.parser, this.beanFactory);
// 获得 GatewayFilter
return filter.apply(tuple);
})
.collect(Collectors.toList()); // 转成 List
// GatewayFilter => OrderedGatewayFilter
ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
for (int i = 0; i < filters.size(); i++) {
ordered.add(new OrderedGatewayFilter(filters.get(i), i+1));
}
// 返回 GatewayFilter 数组
return ordered;
}第 4 至 8 行 :调用
#loadGatewayFilters()方法,使用GatewayProperties.defaultFilters默认的过滤器配置,将 FilterDefinition 转换成 GatewayFilter 。第 10 至 12 行 :调用
#loadGatewayFilters()方法,使用RouteDefinition.filters配置的过滤器配置,将 FilterDefinition 转换成 GatewayFilter 。---------------------------- 分割线 --------------------------
第 18 至 43 行 :
#loadGatewayFilters()方法。第 20 至 34 行 :将 FilterDefinition 转换成 GatewayFilter 。
第 21 至 25 行 :获得 GatewayFilterFactory Bean 对象。
第 27 至 31 行 :调用
#getTuple()方法,获得 Tuple 。该方法比较复杂,在 「2.4 获得 Tuple」 详细解析。第 33 行 :创建 GatewayFilter 。
第 35 行 :获得 GatewayFilter 数组。
第 37 至 40 行 :将 GatewayFilter 数组转换成 OrderedGatewayFilter 数组。在 《Spring-Cloud-Gateway 源码解析 —— 过滤器 (4.1) 之 GatewayFilter 一览》 详细解析。
第 42 行 :返回 GatewayFilter 数组。
2.4 获得 Tuple
在看 #getTuple() 方法的代码实现之前,我们先了解下 Tuple 。
Tuple ,定义如下 :
FROM 《简单实现 Java 的 Tuple 元组数据类型》
元组类型,即 Tuple 常在脚本语言中出现,例如 Scala 的 ("Unmi", "[email protected]", "blahbla")
元组可认为是象数组一样的容器,它的目的是让你方便构造和引用,例如 Pair 可认为是一个只能存两个元素的元组,像是个 Map
真正的元组应该是可以任意多个元素的容器,绕来绕去,它还是数组,或列表,所以我们实现上还是要借助于数组或是列表。
截止目前,Java 并未内置 Tuple 的实现。Spring 提供了 spring-tuple 类库,提供了 Tuple 的支持。使用示例如下 :
// 1 对
Tuple tuple = tuple().of("foo", "bar");
// 2 对
Tuple tuple2 = tuple().of("up", 1, "down", 2);
// 3 对
Tuple tuple3 = tuple().of("up", 1, "down", 2, "charm", 3 );
// 4 对
Tuple tuple4 = tuple().of("up", 1, "down", 2, "charm", 3, "strange", 4);
// 6 对 ( 适用于超过 4 对 )
Tuple tuple6 = tuple().put("up", 1)
.put("down", 2)
.put("charm", 3)
.put("strange", 4)
.put("bottom", 5)
.put("top", 6)
.build();更多
spring-tuple的文档,请见 《Spring Docs —— Tuples》 。
那么为什么 RoutePredicateFactory#apply(Tuple) / GatewayFilterFactory#apply(Tuple) 需要使用 Tuple 呢 ?RoutePredicateFactory / GatewayFilterFactory 子类实现类需要成对的参数不同,例如 :
org.springframework.cloud.gateway.filter.factory.SetStatusGatewayFilterFactory,使用status一对参数。org.springframework.cloud.gateway.filter.factory.SetResponseHeaderGatewayFilterFactory,使用name/value两对参数。
OK ,我们开始看看 #getTuple() 方法,代码如下 :
/* for testing */ static Tuple getTuple(ArgumentHints hasArguments, Map<String, String> args, SpelExpressionParser parser, BeanFactory beanFactory) {
TupleBuilder builder = TupleBuilder.tuple();
// 参数为空
List<String> argNames = hasArguments.argNames();
if (!argNames.isEmpty()) {
// ensure size is the same for key replacement later
if (hasArguments.validateArgs() && args.size() != argNames.size()) {
throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames
+ " " + argNames + ". Found " + args.size() + " " + args + "'");
}
}
// 创建 Tuple
int entryIdx = 0;
for (Map.Entry<String, String> entry : args.entrySet()) {
// 获得参数 KEY
String key = entry.getKey();
// RoutePredicateFactory has name hints and this has a fake key name
// replace with the matching key hint
if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty()
&& entryIdx < args.size()) {
key = argNames.get(entryIdx);
}
// 获得参数 VALUE
Object value;
String rawValue = entry.getValue();
if (rawValue != null) {
rawValue = rawValue.trim();
}
if (rawValue != null && rawValue.startsWith("#{") && entry.getValue().endsWith("}")) {
// assume it's spel
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new BeanFactoryResolver(beanFactory));
Expression expression = parser.parseExpression(entry.getValue(), new TemplateParserContext());
value = expression.getValue(context);
} else {
value = entry.getValue();
}
// 添加 KEY / VALUE
builder.put(key, value);
entryIdx++;
}
Tuple tuple = builder.build();
// 校验参数
if (hasArguments.validateArgs()) {
for (String name : argNames) {
if (!tuple.hasFieldName(name)) {
throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple);
}
}
}
return tuple;
}hasArguments参数,点击org.springframework.cloud.gateway.support.ArgumentHints查看代码实现。RoutePredicateFactory / GatewayFilterFactory 实现 ArgumentHints 接口。第 5 至 12 行 :校验参数是否正确( 需要参数非空 )。
第 15 至 44 行 :创建 Tuple 。
第 18 至 24 行 :获得一对参数 KEY 。
第 26 至 39 行 :获得一对参数 VALUE 。
第 41 行 :添加一对参数 KEY / VALUE 。我们在此处打断点,看看此时各变量的值,路由配置如下 :
spring: cloud: gateway: routes: - id: websocket_test uri: ws://localhost:9000 order: 9000 predicates: - Path=/echo - Query=foo, ba.PATH
/echo
Query
foo
ba.
第 44 行 :创建 Tuple 。
第 47 至 53 行 :校验参数是否正确( 需要参数都存在 )。
第 54 行 :返回 Tuple 。