摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud-Gateway/route-locator-route-definition/ 「芋道源码」欢迎转载,保留摘要,谢谢!
关注 微信公众号:【芋道源码】 有福利:
本文主要分享 RouteDefinitionRouteLocator 的源码实现 。
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator
,基于 RouteDefinitionLocator 的 RouteLocator 实现类 。
RouteDefinitionRouteLocator 从 RouteDefinitionLocator 获取 RouteDefinition ,转换成 Route 。如下图 :
RouteDefinitionRouteLocator 构造方法,代码如下 :
1: public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware{ 2: protected final Log logger = LogFactory.getLog(getClass()); 3: 4: private final RouteDefinitionLocator routeDefinitionLocator; 5: /** 6: * RoutePredicateFactory 映射 7: * key :{@link RoutePredicateFactory#name()} 8: */ 9: private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>(); 10: /** 11: * GatewayFilterFactory 映射 12: * key :{@link GatewayFilterFactory#name()} 13: */ 14: private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>(); 15: private final GatewayProperties gatewayProperties; 16: private final SpelExpressionParser parser = new SpelExpressionParser(); 17: private BeanFactory beanFactory; 18: 19: public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, 20: List<RoutePredicateFactory> predicates, 21: List<GatewayFilterFactory> gatewayFilterFactories, 22: GatewayProperties gatewayProperties){ 23: // 设置 RouteDefinitionLocator 24: this.routeDefinitionLocator = routeDefinitionLocator; 25: // 初始化 RoutePredicateFactory 26: initFactories(predicates); 27: // 初始化 RoutePredicateFactory 28: gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)); 29: // 设置 GatewayProperties 30: this.gatewayProperties = gatewayProperties; 31: } 32: 33: @Override 34: public void setBeanFactory(BeanFactory beanFactory) throws BeansException{ 35: this.beanFactory = beanFactory; 36: } 37:}
routeDefinitionLocator
属性,提供 RouteDefinition 的 RouteDefinitionLocator 。 predicates
属性,RoutePredicateFactory Bean 对象 映射 。
key
为 {@link RoutePredicateFactory#name()}
。 RouteDefinition.predicates
转换成 Route.predicates
。 #initFactories()
方法,初始化映射。逻辑比较简单,点击 链接 查看代码。 gatewayFilterFactories
属性,RoutePredicateFactory Bean 对象 映射 。
key
为 {@link GatewayFilterFactory#name()}
。 RouteDefinition.filters
转换成 Route.filters
。 gatewayProperties
属性,使用 GatewayProperties.defaultFilters
默认过滤器定义数组 ,添加到每个 Route 。下文会看到相关代码的实现。 parser
属性,Spring EL 表达式解析器。在会看到它的使用。 beanFactory
属性,Bean 工厂。 #getRoutes()
方法,获得 Route 数组。代码如下 :
1: @Override 2: public Flux<Route> getRoutes(){ 3: return this.routeDefinitionLocator.getRouteDefinitions() 4: .map(this::convertToRoute) // RouteDefinition => Route 5: //TODO: error handling 6: .map(route -> { // 打印日志 7: if (logger.isDebugEnabled()) { 8: logger.debug("RouteDefinition matched: " + route.getId()); 9: } 10: return route; 11: }); 12: 13: }
RouteDefinitionLocator#getRouteDefinitions()
方法,获得 RouteDefinitions 数组。 #convertToRoute()
方法,将每个 RouteDefinition 转换 成 Route 。该方法在详细解析。 #convertToRoute()
方法,将每个 RouteDefinition 转换 成 Route 。代码如下 :
1: private Route convertToRoute(RouteDefinition routeDefinition){ 2: // 合并 Predicate 3: Predicate<ServerWebExchange> predicate = combinePredicates(routeDefinition); 4: // 获得 GatewayFilter 5: List<GatewayFilter> gatewayFilters = getFilters(routeDefinition); 6: // 构建 Route 7: return Route.builder(routeDefinition) 8: .predicate(predicate) 9: .gatewayFilters(gatewayFilters) 10: .build(); 11: }
#combinePredicates()
方法,将 RouteDefinition.predicates
数组 合并成 一个 java.util.function.Predicate
,这样 RoutePredicateHandlerMapping 为请求 匹配 Route ,只要调用 一次 Predicate#test(ServerWebExchange)
方法即可。 #getFilters()
方法,获得 GatewayFilter 数组 。 #combinePredicates()
方法,代码如下 :
1: private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition){ 2: // 寻找 Predicate 3: List<PredicateDefinition> predicates = routeDefinition.getPredicates(); 4: Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0)); 5: // 拼接 Predicate 6: for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) { 7: Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate); 8: predicate = predicate.and(found); 9: } 10: // 返回 Predicate 11: return predicate; 12: } 13: 14: private Predicate<ServerWebExchange> lookup(RouteDefinition routeDefinition, PredicateDefinition predicate){ 15: // 获得 RoutePredicateFactory 16: RoutePredicateFactory found = this.predicates.get(predicate.getName()); 17: if (found == null) { 18: throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName()); 19: } 20: // 获得 Tuple 21: Map<String, String> args = predicate.getArgs(); 22: if (logger.isDebugEnabled()) { 23: logger.debug("RouteDefinition " + routeDefinition.getId() + " applying " 24: + args + " to " + predicate.getName()); 25: } 26: Tuple tuple = getTuple(found, args, this.parser, this.beanFactory); 27: // 获得 Predicate 28: return found.apply(tuple); 29: }
#lookup()
方法,查找 PredicateDefinition 对应的 Predicate 。为什么拆成 两部分 ?第一部分找到 java.util.function.Predicate
,第二部分通过 Predicate#and(Predicate)
方法不断拼接。 #lookup()
方法。
#getTuple()
方法,获得 Tuple 。该方法比较复杂,在详细解析。 RoutePredicateFactory#apply(Tuple)
方法,创建 Predicate 。在 《Spring-Cloud-Gateway 源码解析 —— 处理器 (3.1) 之 RoutePredicateFactory 路由谓语工厂 》 详细解析。 #getFilters()
方法,代码如下 :
1: private List<GatewayFilter> getFilters(RouteDefinition routeDefinition){ 2: List<GatewayFilter> filters = new ArrayList<>(); 3: // 添加 默认过滤器 4: //TODO: support option to apply defaults after route specific filters? 5: if (!this.gatewayProperties.getDefaultFilters().isEmpty()) { 6: filters.addAll(loadGatewayFilters("defaultFilters", 7: this.gatewayProperties.getDefaultFilters())); 8: } 9: // 添加 配置的过滤器 10: if (!routeDefinition.getFilters().isEmpty()) { 11: filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters())); 12: } 13: // 排序 14: AnnotationAwareOrderComparator.sort(filters); 15: return filters; 16: } 17: 18: private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions){ 19: List<GatewayFilter> filters = filterDefinitions.stream() 20: .map(definition -> { // FilterDefinition => GatewayFilter 21: // 获得 GatewayFilterFactory 22: GatewayFilterFactory filter = this.gatewayFilterFactories.get(definition.getName()); 23: if (filter == null) { 24: throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName()); 25: } 26: // 获得 Tuple 27: Map<String, String> args = definition.getArgs(); 28: if (logger.isDebugEnabled()) { 29: logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName()); 30: } 31: Tuple tuple = getTuple(filter, args, this.parser, this.beanFactory); 32: // 获得 GatewayFilter 33: return filter.apply(tuple); 34: }) 35: .collect(Collectors.toList()); // 转成 List 36: // GatewayFilter => OrderedGatewayFilter 37: ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size()); 38: for (int i = 0; i < filters.size(); i++) { 39: ordered.add(new OrderedGatewayFilter(filters.get(i), i+1)); 40: } 41: // 返回 GatewayFilter 数组 42: return ordered; 43: }
#loadGatewayFilters()
方法,使用 GatewayProperties.defaultFilters
默认 的过滤器配置,将 FilterDefinition 转换成 GatewayFilter 。 #loadGatewayFilters()
方法,使用 RouteDefinition.filters
配置 的过滤器配置,将 FilterDefinition 转换成 GatewayFilter 。 #loadGatewayFilters()
方法。
#getTuple()
方法,获得 Tuple 。该方法比较复杂,在详细解析。 在看 #getTuple()
方法的代码实现之前,我们先了解下 Tuple 。
Tuple , 定义 如下 :
FROM 《简单实现 Java 的 Tuple 元组数据类型》
元组类型,即 Tuple 常在脚本语言中出现,例如 Scala 的 (“Unmi”, “fantasia@sina.com”, “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()
方法,代码如下 :
1: /* for testing */ static Tuple getTuple(ArgumentHints hasArguments, Map<String, String> args, SpelExpressionParser parser, BeanFactory beanFactory){ 2: TupleBuilder builder = TupleBuilder.tuple(); 3: 4: // 参数为空 5: List<String> argNames = hasArguments.argNames(); 6: if (!argNames.isEmpty()) { 7: // ensure size is the same for key replacement later 8: if (hasArguments.validateArgs() && args.size() != argNames.size()) { 9: throw new IllegalArgumentException("Wrong number of arguments. Expected " + argNames 10: + " " + argNames + ". Found " + args.size() + " " + args + "'"); 11: } 12: } 13: 14: // 创建 Tuple 15: int entryIdx = 0; 16: for (Map.Entry<String, String> entry : args.entrySet()) { 17: // 获得参数 KEY 18: String key = entry.getKey(); 19: // RoutePredicateFactory has name hints and this has a fake key name 20: // replace with the matching key hint 21: if (key.startsWith(NameUtils.GENERATED_NAME_PREFIX) && !argNames.isEmpty() 22: && entryIdx < args.size()) { 23: key = argNames.get(entryIdx); 24: } 25: // 获得参数 VALUE 26: Object value; 27: String rawValue = entry.getValue(); 28: if (rawValue != null) { 29: rawValue = rawValue.trim(); 30: } 31: if (rawValue != null && rawValue.startsWith("#{") && entry.getValue().endsWith("}")) { 32: // assume it's spel 33: StandardEvaluationContext context = new StandardEvaluationContext(); 34: context.setBeanResolver(new BeanFactoryResolver(beanFactory)); 35: Expression expression = parser.parseExpression(entry.getValue(), new TemplateParserContext()); 36: value = expression.getValue(context); 37: } else { 38: value = entry.getValue(); 39: } 40: // 添加 KEY / VALUE 41: builder.put(key, value); 42: entryIdx++; 43: } 44: Tuple tuple = builder.build(); 45: 46: // 校验参数 47: if (hasArguments.validateArgs()) { 48: for (String name : argNames) { 49: if (!tuple.hasFieldName(name)) { 50: throw new IllegalArgumentException("Missing argument '" + name + "'. Given " + tuple); 51: } 52: } 53: } 54: return tuple; 55: }
hasArguments
参数 ,点击 org.springframework.cloud.gateway.support.ArgumentHints
查看代码实现。RoutePredicateFactory / GatewayFilterFactory 实现 ArgumentHints 接口 。 第 15 至 44 行 :创建 Tuple 。
第 41 行 :添加 一对 参数 KEY / VALUE 。我们在此处打断点,看看此时各变量的值,路由配置如下 :
spring: cloud: gateway: routes: - id: websocket_test uri: ws://localhost:9000 order: 9000 predicates: - Path=/echo - Query=foo, ba.
/echo
foo
ba.
第 44 行 :创建 Tuple 。
恩恩,干了一些的文章。这个周末还是木有完成计划写的文章。继续加油!
胖友,分享一波朋友圈可好!