Spring Cloud - Openfeign 实现原理分析

OpenFeign简介

OpenFeign 是一个声明式 RESTful 网络请求客户端。OpenFeign 会根据带有注解的函数信息构建出网络请求的模板,在发送网络请求之前,OpenFeign 会将函数的参数值设置到这些请求模板中。虽然 OpenFeign 只能支持基于文本的网络请求,但是它可以极大简化网络请求的实现,方便编程人员快速构建自己的网络请求应用。

核心组件与概念

在阅读源码时,可以沿着两条线路进行,一是被@FeignClient注解修饰的接口类如何创建,也就是其 Bean 实例是如何被创建的;二是调用这些接口类的网络请求相关函数时,OpenFeign 是如何发送网络请求的。而 OpenFeign 相关的类也可以以此来进行分类,一部分是用来初始化相应的 Bean 实例的,一部分是用来在调用方法时发送网络请求。

动态注册BeanDefinition

1. FeignClientsRegistrar

@EnableFeignClients 有三个作用,一是引入FeignClientsRegistrar;二是指定扫描FeignClient的包信息,就是指定FeignClient接口类所在的包名;三是指定FeignClient接口类的自定义配置类。@EnableFeignClients注解的定义如下所示:

public @interface EnableFeignClients {// 下面三个函数都是为了指定需要扫描的包String[] value() default {};String[] basePackages() default {};Class<?>[] basePackageClasses() default {};// 指定自定义feign client的自定义配置,可以配置 Decoder、Encoder和Contract等组件// FeignClientsConfiguration 是默认的配置类Class<?>[] defaultConfiguration() default {};// 指定被@FeignClient修饰的类,如果不为空,那么路径自动检测机制会被关闭Class<?>[] clients() default {};
}
// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 从 EnableFeignClients 的属性值来构建 Feign 的自定义 Configuration 进行注册registerDefaultConfiguration(metadata, registry);// 扫描 package , 注册被 @FeignClient 修饰的接口类的Bean信息registerFeignClients(metadata, registry);
}
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {// 使用 BeanDefinitionBuilder 来生成 BeanDefinition, 并注册到 registry 上BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());
}

FeignClientSpecification 类实现了 NamedContextFactory.Specification 接口,它是 OpenFeign 组件实例化的重要一环,它持有自定义配置类提供的组件实例,供 OpenFeign 使用。SpringCloud 框架使用 NamedContextFactory 创建一系列的运行上下文,来让对应的 Specification 在这些上下文中创建实例对象。

// org.springframework.cloud.openfeign.FeignAutoConfiguration
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();@Bean
public FeignContext feignContext() {// 创建 FeignContext 实例, 并将 FeignClientSpecification 注入FeignContext context = new FeignContext();context.setConfigurations(this.configurations);return context;
}
// org.springframework.cloud.openfeign.FeignContext#FeignContext
public FeignContext() {// 将默认的 FeignClientsConfiguration 作为参数传递给构造函数super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}

NamedContextFactory 是 FeignContext 的父类, 其 createContext 方法会创建具有名称为 Spring 的AnnotationConfigApplicationContext 实例作为当前上下文的子上下文。这些 AnnotationConfigApplicationContext 实例可以管理 OpenFeign 组件的不同实例。

NamedContextFactory 的实现代码如下:

// org.springframework.cloud.context.named.NamedContextFactory#createContext
protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();// 获取该 name 所对应的 configuration ,如果有的话,就注册到子 context 中if (this.configurations.containsKey(name)) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}// 注册 default 的 Configuration, 也就是 FeignClientsRegistrar 类的 registerDefaultConfiguration 方法中注册的Configurationfor (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}// 注册 PropertyPlaceholderAutoConfiguration 和 FeignClientsConfiguration 配置类context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);// 设置子 context 的 Environment 的 propertySource 属性源context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object>singletonMap(this.propertyName, name)));// 所有 context 的 parent 都相同,这样的话,一些相同的Bean可以通过 parent context 来获取if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);// jdk11 issue// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101context.setClassLoader(this.parent.getClassLoader());}context.setDisplayName(generateDisplayName(name));context.refresh();return context;
}

2. 扫描类信息

FeignClientsRegistrar 做的第二件事情是扫描指定包下的类文件,注册 @FeignClient 注解修饰的接口类信息。

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients
public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {// 自定义扫描类ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;// 获取 EnableFeignClients 配置信息Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());// 依照 Annotation 来进行 TypeFilter ,只会扫描出被 FeignClient 修饰的类AnnotationTypeFilter annotationTypeFilter = n

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

六西格玛绿带培训:量化进步的关键

在追求卓越的道路上&#xff0c;六西格玛绿带培训成为了一种革命性的思维方式&#xff0c;让我们能够以科学和系统的方法提升过程性能。但在这个追求中&#xff0c;我们如何确定我们的进步&#xff1f;过程能力分析为我们提供了明确的答案。通过计算过程能力&#xff0c;即6σ&…

竞赛 python 爬虫与协同过滤的新闻推荐系统

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python 爬虫与协同过滤的新闻推荐系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&…

python关于字符串基础学习

字符串 python字符串是不可改变的 Python不支持单字符类型&#xff0c;单字符也是作为一个字符串使用的。 字符串编码 python3直接支持Unicode,可以表示世界上任何书面语言的字符 python3的字符默认就是16位Unicode编码&#xff0c;ASCII是Unicode的子集 使用内置函数 ord()…

二十六 超级数据查看器 讲解稿 用输入值批量更新字段

二十六 超级数据查看器 讲解稿 用输入值批量更新字段 ​点击此处 以新页面 打开B站 播放当前教学视频 点击访问app下载页面 百度手机助手 下载地址 ​ 大家好&#xff0c;今天我们讲一下超级数据查看器的输入更新功能&#xff0c;输入更新功能是将选择的TXT文档的数据&…

【竞技宝】DOTA2-PGL联赛:niu神无解 LGD2-0轻松击败DH

北京时间2024年3月26日,PGL联赛中国区的比赛在昨日正式打响,首日共进行了四场胜者组首轮的比赛,第四场比赛由LGD对阵DH。本场比赛,DH两局都在前中期和LGD有来有回,但niu的中期节奏完全摧毁了DH,最终LGD2-0轻松击败DH。以下是本场比赛的详细战报。 第一局: 首局比赛,LGD在天辉方…

使用Docker Compose一键部署前后端分离项目(图文保姆级教程)

一、安装Docker和docker Compose 1.Docker安装 //下载containerd.io包 yum install https://download.docker.com/linux/fedora/30/x86_64/stable/Packages/containerd.io-1.2.6-3.3.fc30.x86_64.rpm //安装依赖项 yum install -y yum-utils device-mapper-persistent-data l…

基于springboot实现课程作业管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现课程作业管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;课程作业管理系统当然也不能排除在外。课程作业管理系统是以实际运用为开发背景…

OpenCV4.9关于矩阵上的掩码操作

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:如何使用OpenCV扫描图像、查找表和时间测量 下一篇:OpenCV4.9的是如何进行图像操作 引言&#xff1a; 矩阵上的掩码操作非常简单。这个想法是&#xff0c;我们根据掩码矩阵&#xff08…

【机器学习之---数学】随机游走

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 随机游走 1. 概念 1.1 例1 在你的饮食俱乐部度过了一个富有成效的晚上后&#xff0c;你在不太清醒的状态下离开了。因此&#xff0c;你会醉醺醺地在展…

Day21|二叉树part07:530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

530. *二叉搜索树的最小绝对差&#xff08;双指针题型&#xff09; 众所周知二叉搜索树的中序遍历序列是一个有序数组&#xff0c;因此最基本的方法就是遍历得到中序序列再进行计算&#xff0c;实际上可以用双指针法&#xff0c;记录中序遍历前一个指针和当前指针的差值&#…

GPT-SoVITS教程,接入酒馆AI,SillyTavern-1.11.5,让AI女友声若幽兰

本次分享一下如何将GPT-SoVITS接入SillyTavern-1.11.5项目&#xff0c;让让AI女友声若幽兰&#xff0c;首先明确一下&#xff0c;SillyTavern-1.11.5只是一个前端项目&#xff0c;它没有任何大模型文本生成能力&#xff0c;所以后端必须有一个api服务来流式生成对话文本&#x…

10.测试教程-性能测试loadrunner1

文章目录 1.LoadRunner的安装2.Loadrunner的基本概念3.开发测试脚本3.1录制基本的用户脚本3.2插入事务3.3插入集合点3.4参数化输入3.5插入函数3.6插入检查点3.7运行时设置3.8单机运行测试脚本 4.玩转三个测试工具4.1Virtual User Generator(此处讲解和3.1是一致的)4.2Controlle…