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

  我们今天的主角是Gateway网关,一听名字就知道它基本的任务就是去分发路由。根据不同的指定名称去请求各个服务,下面是Gateway官方的解释:

Spring Cloud Gateway,其他的博主就不多说了,大家多去官网看看,只有官方的才是最正确的,回归主题,我们的过滤器与断言如何加载进来的,并且是如何进行对请求进行过滤的。

  大家如果对SpringBoot自动加载的熟悉的话,一定知道要看一个代码的源码,要找到META-INF下的spring.factories,具体为啥的博主就不多说了,网上也有很多讲解自动加载的源码分析,今天就讲解Gateway,所有项目三板斧:加依赖、写注解、弄配置

  依赖:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>

  注解:启动类上需要添加@EnableDiscoveryClient,启动服务发现

  配置:

spring:cloud:gateway:routes:- id: after-route #id必须要唯一uri: lb://product-centerpredicates:- After=2030-12-16T15:53:22.999+08:00[Asia/Shanghai]filters:- PrefixPath=/product-api    

  大家看到这个配置的时候,为什么我们写After断言与PrefixPath过滤器,gateway就会自动识别呢,那我们有没有那一个地方可以看到所有的自带的属性呢?当然有,而且我们本篇就主要讲解为什么gateway会自动识别,并且我们要自己实现并且添加自定义属性。开始源码解析第一步,找到自动加载的类一探究竟;

   看到这里的时候,第一步就成功了,剩下的就是找到org.springframework.cloud.gateway.config.GatewayAutoConfiguration这个关键类,我们主要看看里面的两个类

    @Beanpublic RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,List<GatewayFilterFactory> GatewayFilters,List<RoutePredicateFactory> predicates,RouteDefinitionLocator routeDefinitionLocator) {return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties);}@Bean@Primary//TODO: property to disable composite?public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));}

  这俩个类配置,大家可能非常熟悉,大家上手一个新知识点的时候,肯定会找一些快速入门的文章看看,博主还是习惯直接找官方的quick start来看,大家可以看看这些快速上手项目:Getting Started | Building a Gateway

  所以博主直接就找到了RouteLocator这个类配置,果不其然,我们找到了断言与过滤器的注入,虽然实在方法体内作为参数传入,但是会被spring解析到,直接去工厂里拿到,具体怎么拿呢?我们再来看看:

 1 public BeanWrapper instantiateUsingFactoryMethod(2             String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {3 4         .....5 6             for (Method candidate : candidates) {7                 Class<?>[] paramTypes = candidate.getParameterTypes();8 9                 if (paramTypes.length >= minNrOfArgs) {
10                     ArgumentsHolder argsHolder;
11 
12                     if (explicitArgs != null) {
13                         // Explicit arguments given -> arguments length must match exactly.
14                         if (paramTypes.length != explicitArgs.length) {
15                             continue;
16                         }
17                         argsHolder = new ArgumentsHolder(explicitArgs);
18                     }
19                     else {
20                         // Resolved constructor arguments: type conversion and/or autowiring necessary.
21                         try {
22                             String[] paramNames = null;
23                             ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
24                             if (pnd != null) {
25                                 paramNames = pnd.getParameterNames(candidate);
26                             }
27                             //主要就是会进入到这里去解析每一个参数类型
28                             argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
29                                     paramTypes, paramNames, candidate, autowiring, candidates.length == 1);
30                         }
31                         catch (UnsatisfiedDependencyException ex) {
32                             if (logger.isTraceEnabled()) {
33                                 logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
34                             }
35                             // Swallow and try next overloaded factory method.
36                             if (causes == null) {
37                                 causes = new LinkedList<>();
38                             }
39                             causes.add(ex);
40                             continue;
41                         }
42                     }
43 
44                     int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
45                             argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
46                     // Choose this factory method if it represents the closest match.
47                     if (typeDiffWeight < minTypeDiffWeight) {
48                         factoryMethodToUse = candidate;
49                         argsHolderToUse = argsHolder;
50                         argsToUse = argsHolder.arguments;
51                         minTypeDiffWeight = typeDiffWeight;
52                         ambiguousFactoryMethods = null;
53                     }
54                     // Find out about ambiguity: In case of the same type difference weight
55                     // for methods with the same number of parameters, collect such candidates
56                     // and eventually raise an ambiguity exception.
57                     // However, only perform that check in non-lenient constructor resolution mode,
58                     // and explicitly ignore overridden methods (with the same parameter signature).
59                     else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
60                             !mbd.isLenientConstructorResolution() &&
61                             paramTypes.length == factoryMethodToUse.getParameterCount() &&
62                             !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
63                         if (ambiguousFactoryMethods == null) {
64                             ambiguousFactoryMethods = new LinkedHashSet<>();
65                             ambiguousFactoryMethods.add(factoryMethodToUse);
66                         }
67                         ambiguousFactoryMethods.add(candidate);
68                     }
69                 }
70             }
71 
72             .....
73         return bw;
74     }

  每一个参数都需要解析,但是看这里不像没关系,继续往下走:就会看到

    private ArgumentsHolder createArgumentArray(String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {....//这下就是了,每个参数都被进行解析for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {....try {//我们的参数就是在这里被进行解析的--resolveAutowiredArgumentObject autowiredArgument = resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, converter, fallback);args.rawArguments[paramIndex] = autowiredArgument;args.arguments[paramIndex] = autowiredArgument;args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();args.resolveNecessary = true;}catch (BeansException ex) {throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);}}}//其他不重要的,直接忽略掉...return args;}

  开始解析的时看到了,我们需要把断言和过滤器列表都加在进来,那spring是如何加载的呢?是根据方法体内传入的类型找到所有实现了断言和过滤器工厂接口的类并且进行获取实例,我们仔细这些工厂的实现类,就会找到我们的使用的一些属性,比如我们例子中的PrefixPath过滤器和Path断言;

    protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {//主要的就是这个,beanNamesForTypeIncludingAncestors方法,该方法就是从bean工厂中获取所有当前类的实现实例名称,String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);...//遍历名称,进行实例化for (String candidate : candidateNames) {if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}.....return result;}

 

   这下我们知道了,系统配置的断言和过滤器是如何被加载 的了,那我们还有一个问题,如果我自定义一个,如何被系统识别呢?并且怎么进行配置呢?不难发现我们之前看源码时,他是被spring通过找工厂实现类找到并且加载进来的,那我们自己实现工厂接口并且使用@Component注解,让spring加载进来不就的了吗?但是你会发现系统自定义的属性断言或者过滤器都有工厂名字的后缀,这是为什么呢?影响我们自定义 的类被加载到gateway中且生效吗?事实是会影响,那为什么影响呢?我们还是看源码。因为我们之前的类加载还没有看完,我们最开始的时候就找到了两个@bean 的自动加载,那这两个类实例化的时候都做了哪些工作,我们还没有细看;

    public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,List<RoutePredicateFactory> predicates,List<GatewayFilterFactory> gatewayFilterFactories,GatewayProperties gatewayProperties) {this.routeDefinitionLocator = routeDefinitionLocator;initFactories(predicates);gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));this.gatewayProperties = gatewayProperties;}

  initFactories(predicates):这段代码主要是进行解析断言工厂实现类;并且放入一个Map中,

  gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)):跟断言的代码几乎一样,因为没有其他多余的逻辑,所以没有封装到方法中,直接使用java8 的流特性,写完了遍历的过程。大家要注意一段代码就是factory.name(),这里使用了一个方法;

    default String name() {return NameUtils.normalizeRoutePredicateName(getClass());}

  主要就是把当前类包含工厂名字的部分去掉了,然后用剩下的字符串当key值,所以我们可以使用工厂名字做后坠,也可以不用,但是剩下的字符则是你要写进配置的关键字,不过博主基本都是按照系统自带属性一样,用的是工厂接口的名字做的后缀。

   好了,今天就讲解这么多,下次在讲解gateway接到请求后,是如何进行一步一步过滤的,何时进行断言校验的。一次不讲这么多,消化了就好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1483776.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

<数据集>蛋壳裂缝检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2520张 标注数量(xml文件个数)&#xff1a;2520 标注数量(txt文件个数)&#xff1a;2520 标注类别数&#xff1a;2 标注类别名称&#xff1a;[crack, egg] 序号类别名称图片数框数1crack245128352egg25142514 使…

最新 Docker 下载镜像超时解决方案:Docker proxy

现在Docker换源也下载失败太常见了&#xff0c;至于原因&#xff0c;大家懂得都懂。本文提供一种简洁的方案&#xff0c; 利用 Docker 的http-proxy&#xff0c;代理至本机的 proxy。 文章目录 前言Docker proxy 前言 这里默认你会安装 clash&#xff0c;然后有配置和数据库。…

LLM大模型实战项目--基于Stable Diffusion的电商平台虚拟试衣

本文详细讲解LLM大模型实战项目&#xff0c;基于Stable Diffusion的电商平台虚拟试衣 一、项目介绍 二、阿里PAI平台介绍 三、阿里云注册及开通PAI 四、PAI_DSW环境搭建 五、SDLORA模型微调 一、项目介绍 AI虚拟试衣是一种创新的技术&#xff0c;利用人工智能和计算机视觉技…

科技核心~书法用纸结合

书法用纸******对墨迹扩散的影响 传统书法用纸制作****与现代改进 书法用纸的*****表面结构关系研究

ARM架构(二)—— arm v7/v8/v9寄存器介绍

1、ARM v7寄存器 1.1 通用寄存器 V7 V8开始 FIQ个IRQ优先级一样&#xff0c; 通用寄存器&#xff1a;31个 1.2 程序状态寄存器 CPSR是程序状态毒存器&#xff0c;保存条件标志位&#xff0c;中断禁止位&#xff0c;当前处理器模式等控制和状态位。每种异常模式下还存在SPSR&…

网络连接—HTTP

HTTP请求数据 导入http模块 import http from ohos.net.http 使用http模块发送请求&#xff0c;处理响应 创建一个http的请求对象&#xff0c;不可复用 let httpRequest http.createHttp() 发起网络请求 HttpRequest.request(http://localhos t:8080/users, //请求U…

报错:QSqlDatabase: QSQLITE driver not loaded...

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

Unity 导入MRTK,使用URP 升级材质,MRTK的材质还是洋红色

控制台显示信息 ToggleBackground material was not upgraded. There’s no upgrader to convert Mixed Reality Toolkit/Standard shader to selected pipeline UnityEditor.Rendering.Universal.UniversalRenderPipelineMaterialUpgrader:UpgradeProjectMaterials() (at 点击…

Linux系统学习日记——vim操作手册

Vim编辑器是linux下的一个命令行编辑器&#xff0c;类似于我们windows下的记事本。 目录 打开文件 编辑 保存退出 打开文件 打开 hello.c不存在也可以打开&#xff0c;保存时vim会自动创建。 效果 Vim打开时&#xff0c;处于命令模式&#xff0c;即执行命令的模式&#x…

Hadoop-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; HadoopHDFSMapReduceHiveFlumeSqoopZookeeperHBaseRedis 章节内容 上一节我们完成了&#xff1a; HBase …

在国产芯片上实现YOLOv5/v8图像AI识别-【1.3】YOLOv5的介绍及使用(训练、导出)更多内容见视频

本专栏主要是提供一种国产化图像识别的解决方案&#xff0c;专栏中实现了YOLOv5/v8在国产化芯片上的使用部署&#xff0c;并可以实现网页端实时查看。根据自己的具体需求可以直接产品化部署使用。 B站配套视频&#xff1a;https://www.bilibili.com/video/BV1or421T74f 数据…

01 安装

安装和卸载中&#xff0c;用户全部切换为root&#xff0c;一旦安装&#xff0c;普通用户也能使用 初期不进行用户管理&#xff0c;全部用root进行&#xff0c;使用mysql语句 1. 卸载内置环境 检查是否有mariadb存在&#xff0c;存在走a部分卸载 ps axj | grep mysql ps ajx |…

前端:Vue学习-2

前端&#xff1a;Vue学习-2 1. vue的生命周期2. 工程化开发和脚手架Vue CLI2.1 组件化开发2.2 scoped解决样式冲突2.3 data是一个函数2.4 组件通信2.5 非父子通信- event bus事件&#xff0c;provide&inject 3.v-model原理->实现父子组件双向绑定4. sync 修饰符->实现…

C++ ───List的使用

目录 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list的迭代器失效 1.1 list的介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&…

亚马逊自发货erp,虚拟自动化发货功能以及1688订单采购

亚马逊自发货erp自动化功能&#xff0c;自动同步订单&#xff0c;1688订单同步。 大家好&#xff0c;今天分享一个非常实用并且节省时间的功能&#xff1a;自动化发货以及1688同步订单。 首先来看下自动化发货功能怎么操作。 →要在商品信息里面添加商品信息&#xff0c;上传…

sql常见50道查询练习题

sql常见50道查询练习题 1. 表创建1.1 表创建1.2 数据插入 2. 简单查询例题(3题&#xff09;2.1 查询"李"姓老师的数量2.2 查询男生、女生人数2.3 查询名字中含有"风"字的学生信息 3. 日期相关例题(6题&#xff09;3.1 查询各学生的年龄3.2 查询本周过生日的…

CentOS6minimal安装nginx-1.26.1.tar.gz 笔记240718

CentOS6安装新版nginx 240718, CentOS6.1-minimal 安装 nginx-1.26.1.tar.gz 下载 nginx-1.26.1.tar.gz 的页面 : https://nginx.org/en/download.html 下载 nginx-1.26.1.tar.gz : https://nginx.org/download/nginx-1.26.1.tar.gz CentOS6.1已过期, 给它更换yum源, 将下面…

用 Bytebase 实现可回滚的数据库数据变更

在修改数据库的数据时&#xff0c;建议先备份即将修改的数据&#xff0c;以便在需要时能够恢复。Bytebase 提供了「数据回滚」的功能来帮助实现这一功能。本教程将为你演示这一过程。 准备 请确保已安装 Docker&#xff0c;如果本地没有重要的现有 Bytebase 数据&#xff0c;可…

github上的工程如何下载子模块.gitmodules如何下载指定的模块download submodules开源项目子模块下载externals

github上的工程如何下载子模块.gitmodules如何下载指定的模块download submodules 说明(废话)解决方案无法执行下载子模块无法下载子项目 说明(废话) 今天在编译一个开源库时&#xff0c;该开源库依赖其他项目&#xff0c;并且项目还挺多的&#xff0c;所以有此解决方案 在编…

IDEA2023版本创建JavaWeb项目及配置Tomcat详细步骤!

一、创建JavaWeb项目 第一步 之前的版本能够在创建时直接选成Web项目&#xff0c;但是2023版本在创建项目时没有该选项&#xff0c;需要在创建项目之后才能配置&#xff0c;首先先创建一个项目。 第二步 在创建好的项目中选中项目后&#xff08;一定要注意选中项目名称然后继…