源码分析SpringCloud Gateway怎样加载断言(predicates)与过滤器(filters)

[复制链接]
发表于 2026-2-22 02:50:45 | 显示全部楼层 |阅读模式
  我们本日的主角是Gateway网关,一听名字就知道它根本的使命就是去分发路由。根据差别的指定名称去哀求各个服务,下面是Gateway官方的表明:
Spring Cloud Gateway,其他的博主就不多说了,各人多去官网看看,只有官方的才是最精确的,回归主题,我们的过滤器与断言怎样加载进来的,而且是怎样举行对哀求举行过滤的。
  各人如果对SpringBoot自动加载的认识的话,肯定知道要看一个代码的源码,要找到META-INF下的spring.factories,具体为啥的博主就不多说了,网上也有很多解说自动加载的源码分析,本日就解说Gateway,全部项目三板斧:加依靠、写注解、弄设置
  依靠:
  1. <dependency>
  2.             <groupId>org.springframework.cloud</groupId>
  3.             <artifactId>spring-cloud-starter-gateway</artifactId>
  4.         </dependency>
复制代码
  注解:启动类上须要添加@EnableDiscoveryClient,启动服务发现
  设置:
  1. spring:
  2.   cloud:
  3.     gateway:
  4.       routes:
  5.        - id: after-route #id必须要唯一
  6.          uri: lb://product-center
  7.          predicates:
  8.           - After=2030-12-16T15:53:22.999+08:00[Asia/Shanghai]
  9.         filters:
  10.           - PrefixPath=/product-api   
复制代码
  各人看到这个设置的时间,为什么我们写After断言与PrefixPath过滤器,gateway就会自动辨认呢,那我们有没有那一个地方可以看到全部的自带的属性呢?固然有,而且我们本篇就紧张解说为什么gateway会自动辨认,而且我们要本身实现而且添加自界说属性。开始源码剖析第一步,找到自动加载的类一探究竟;




   看到这里的时间,第一步就乐成了,剩下的就是找到org.springframework.cloud.gateway.config.GatewayAutoConfiguration这个关键类,我们紧张看看内里的两个类
  1.     @Bean
  2.     public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
  3.                                                    List<GatewayFilterFactory> GatewayFilters,
  4.                                                    List<RoutePredicateFactory> predicates,
  5.                                                    RouteDefinitionLocator routeDefinitionLocator) {
  6.         return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties);
  7.     }
  8.     @Bean
  9.     @Primary
  10.     //TODO: property to disable composite?
  11.     public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
  12.         return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
  13.     }
复制代码
  这俩个类设置,各人大概非常认识,各人上手一个新知识点的时间,肯定会找一些快速入门的文章看看,博主照旧风俗直接找官方的quick start来看,各人可以看看这些快速上手项目:Getting Started | Building a Gateway
  以是博主直接就找到了RouteLocator这个类设置,果不其然,我们找到了断言与过滤器的注入,固然着实方法体内作为参数传入,但是会被spring剖析到,直接去工厂里拿到,具体怎么拿呢?我们再来看看:
  1. 1 public BeanWrapper instantiateUsingFactoryMethod(
  2. 2             String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
  3. 3
  4. 4         .....
  5. 5
  6. 6             for (Method candidate : candidates) {
  7. 7                 Class<?>[] paramTypes = candidate.getParameterTypes();
  8. 8
  9. 9                 if (paramTypes.length >= minNrOfArgs) {
  10. 10                     ArgumentsHolder argsHolder;
  11. 11
  12. 12                     if (explicitArgs != null) {
  13. 13                         // Explicit arguments given -> arguments length must match exactly.
  14. 14                         if (paramTypes.length != explicitArgs.length) {
  15. 15                             continue;
  16. 16                         }
  17. 17                         argsHolder = new ArgumentsHolder(explicitArgs);
  18. 18                     }
  19. 19                     else {
  20. 20                         // Resolved constructor arguments: type conversion and/or autowiring necessary.
  21. 21                         try {
  22. 22                             String[] paramNames = null;
  23. 23                             ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
  24. 24                             if (pnd != null) {
  25. 25                                 paramNames = pnd.getParameterNames(candidate);
  26. 26                             }
  27. 27                             //主要就是会进入到这里去解析每一个参数类型
  28. 28                             argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
  29. 29                                     paramTypes, paramNames, candidate, autowiring, candidates.length == 1);
  30. 30                         }
  31. 31                         catch (UnsatisfiedDependencyException ex) {
  32. 32                             if (logger.isTraceEnabled()) {
  33. 33                                 logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
  34. 34                             }
  35. 35                             // Swallow and try next overloaded factory method.
  36. 36                             if (causes == null) {
  37. 37                                 causes = new LinkedList<>();
  38. 38                             }
  39. 39                             causes.add(ex);
  40. 40                             continue;
  41. 41                         }
  42. 42                     }
  43. 43
  44. 44                     int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
  45. 45                             argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
  46. 46                     // Choose this factory method if it represents the closest match.
  47. 47                     if (typeDiffWeight < minTypeDiffWeight) {
  48. 48                         factoryMethodToUse = candidate;
  49. 49                         argsHolderToUse = argsHolder;
  50. 50                         argsToUse = argsHolder.arguments;
  51. 51                         minTypeDiffWeight = typeDiffWeight;
  52. 52                         ambiguousFactoryMethods = null;
  53. 53                     }
  54. 54                     // Find out about ambiguity: In case of the same type difference weight
  55. 55                     // for methods with the same number of parameters, collect such candidates
  56. 56                     // and eventually raise an ambiguity exception.
  57. 57                     // However, only perform that check in non-lenient constructor resolution mode,
  58. 58                     // and explicitly ignore overridden methods (with the same parameter signature).
  59. 59                     else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
  60. 60                             !mbd.isLenientConstructorResolution() &&
  61. 61                             paramTypes.length == factoryMethodToUse.getParameterCount() &&
  62. 62                             !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
  63. 63                         if (ambiguousFactoryMethods == null) {
  64. 64                             ambiguousFactoryMethods = new LinkedHashSet<>();
  65. 65                             ambiguousFactoryMethods.add(factoryMethodToUse);
  66. 66                         }
  67. 67                         ambiguousFactoryMethods.add(candidate);
  68. 68                     }
  69. 69                 }
  70. 70             }
  71. 71
  72. 72             .....
  73. 73         return bw;
  74. 74     }
复制代码
  每一个参数都须要剖析,但是看这里不像不要紧,继续往下走:就会看到
  1.     private ArgumentsHolder createArgumentArray(
  2.             String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
  3.             BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
  4.             boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
  5.         ....
  6.         //这下就是了,每个参数都被进行解析
  7.         for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
  8.             ....
  9.                 try {
  10.                 //我们的参数就是在这里被进行解析的--resolveAutowiredArgument
  11.                     Object autowiredArgument = resolveAutowiredArgument(
  12.                             methodParam, beanName, autowiredBeanNames, converter, fallback);
  13.                     args.rawArguments[paramIndex] = autowiredArgument;
  14.                     args.arguments[paramIndex] = autowiredArgument;
  15.                     args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();
  16.                     args.resolveNecessary = true;
  17.                 }
  18.                 catch (BeansException ex) {
  19.                     throw new UnsatisfiedDependencyException(
  20.                             mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
  21.                 }
  22.             }
  23.         }
  24.         //其他不重要的,直接忽略掉
  25.         ...
  26.         return args;
  27.     }
复制代码
  开始剖析的时看到了,我们须要把断言和过滤器列表都加在进来,那spring是怎样加载的呢?是根据方法体内传入的范例找到全部实现了断言和过滤器工厂接口的类而且举行获取实例,我们过细这些工厂的实现类,就会找到我们的利用的一些属性,比如我们例子中的PrefixPath过滤器和Path断言;
  1.     protected Map<String, Object> findAutowireCandidates(
  2.             @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
  3.         //主要的就是这个,beanNamesForTypeIncludingAncestors方法,该方法就是从bean工厂中获取所有当前类的实现实例名称,
  4.         String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
  5.                 this, requiredType, true, descriptor.isEager());
  6.         Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
  7.         ...
  8.         //遍历名称,进行实例化
  9.         for (String candidate : candidateNames) {
  10.             if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
  11.                 addCandidateEntry(result, candidate, descriptor, requiredType);
  12.             }
  13.         }
  14.         .....
  15.         return result;
  16.     }
复制代码



 


   这下我们知道了,体系设置的断言和过滤器是怎样被加载 的了,那我们尚有一个标题,如果我自界说一个,怎样被体系辨认呢?而且怎么举行设置呢?不难发现我们之前看源码时,他是被spring通过找工厂实现类找到而且加载进来的,那我们本身实现工厂接口而且利用@Component注解,让spring加载进来不就的了吗?但是你会发现体系自界说的属性断言大概过滤器都有工厂名字的后缀,这是为什么呢?影响我们自界说 的类被加载到gateway中且见效吗?究竟是会影响,那为什么影响呢?我们照旧看源码。由于我们之前的类加载还没有看完,我们最开始的时间就找到了两个@bean 的自动加载,那这两个类实例化的时间都做了哪些工作,我们还没有细看;
  1.     public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
  2.                                        List<RoutePredicateFactory> predicates,
  3.                                        List<GatewayFilterFactory> gatewayFilterFactories,
  4.                                        GatewayProperties gatewayProperties) {
  5.         this.routeDefinitionLocator = routeDefinitionLocator;
  6.         initFactories(predicates);
  7.         gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));
  8.         this.gatewayProperties = gatewayProperties;
  9.     }
复制代码
  initFactories(predicates):这段代码紧张是举行剖析断言工厂实现类;而且放入一个Map中,
  gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)):跟断言的代码险些一样,由于没有其他多余的逻辑,以是没有封装到方法中,直接利用java8 的流特性,写完了遍历的过程。各人要注意一段代码就是factory.name(),这里利用了一个方法;
  1.     default String name() {
  2.         return NameUtils.normalizeRoutePredicateName(getClass());
  3.     }
复制代码
  紧张就是把当前类包罗工厂名字的部门去掉了,然后用剩下的字符串当key值,以是我们可以利用工厂名字做后坠,也可以不消,但是剩下的字符则是你要写进设置的关键字,不外博主根本都是按照体系自带属性一样,用的是工厂接口的名字做的后缀。
   好了,本日就解说这么多,下次在解说gateway接到哀求后,是怎样举行一步一步过滤的,何时举行断言校验的。一次不讲这么多,消化了就好。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表