[SpringCloud] Feign Client 的创建 (二) (五)

文章目录

      • 1.自动配置FeignAutoConfiguration
      • 2.生成 Feign Client
        • 2.1 从Feign Client子容器获取组件
        • 2.2 Feign Client子容器的创建
        • 2.3 构建Feign Client实例

1.自动配置FeignAutoConfiguration

spring-cloud-starter-openfeign 包含了 spring-cloud-openfeign-core

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

FeignAutoConfiguration:

在这里插入图片描述

  • FeignClientSpecification: FeignClient的配置类。
  • FeignContext: Spring容器中所有的FeignClient规范类实例都放入了FeignContext。其中存在两个map。

在这里插入图片描述

2.生成 Feign Client

FeignClientFactoryBean: 就是Spring的FactoryBean。

在这里插入图片描述

在这里插入图片描述

2.1 从Feign Client子容器获取组件

FeignClientFactoryBean.getObject():

//FeignClientFactoryBean.java
public Object getObject() throws Exception {return getTarget();
}
//FeignClientFactoryBean.java
<T> T getTarget() {//根据spring容器,获取FeignContext,Feign的上下文,也是FeignClient的工厂类FeignContext context = this.applicationContext.getBean(FeignContext.class);//根据FeignContext,获取一个Feign的构建器Feign.Builder builder = feign(context);...return (T) targeter.target(this, builder, context,new HardCodedTarget<>(this.type, this.name, url));
}
  1. 根据spring容器, 获取FeignContext, 是FeignClient的工厂类。
  2. 根据FeignContext, 获取Feign的构造器。

在这里插入图片描述

feign: 从Feign Client子容器获取组件。

//FeignClientFactoryBean.java
protected Feign.Builder feign(FeignContext context) {//get方法:从FeignContext中获取对应类型的实例,底层会从当前FeignClient对应的子容器中获取//这里获取Feign的日志工厂FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);Logger logger = loggerFactory.create(this.type);// @formatter:off//这里获取Feign的构建器//构建器的意义我们不需要关注复杂的构建流程,只需要给构建器传递一些需要的组件即可//这里主要往构建器放入一些FeignClient依赖的一些组件Feign.Builder builder = get(context, Feign.Builder.class)// required values.logger(logger).encoder(get(context, Encoder.class)).decoder(get(context, Decoder.class)).contract(get(context, Contract.class));// @formatter:on//获取FeignClientProperties进行一些属性的配置configureFeign(context, builder);return builder;
}//看其中一个get方法:
//FeignClientFactoryBean.java
protected <T> T get(FeignContext context, Class<T> type) {//注意,当前类是FeignClientFactoryBean//所以这个this.contextId实际上是当前FeignClient的服务id、微服务名称T instance = context.getInstance(this.contextId, type);if (instance == null) {throw new IllegalStateException("No bean found of type " + type + " for " + this.contextId);}return instance;
}

在这里插入图片描述

//NamedContextFactory.java,就是FeignContext.java
public <T> T getInstance(String name, Class<T> type) {//根据name先获取对应的子容器//name就是微服务名称,FeignClient的名称AnnotationConfigApplicationContext context = getContext(name);//根据类型从当前子容器,和子容器所有的祖先容器中查找bean的名称,判断是否存在if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,type).length > 0) {//存在就返回对应类型的实例return context.getBean(type);}return null;
}
2.2 Feign Client子容器的创建

获取子容器, 如果获取不到的话则创建子容器。

getContext -> createContext:

在这里插入图片描述

//NamedContextFactory.java 
//FeignContext.java继承自NamedContextFactory.java
protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {//双重检查锁,线程安全问题if (!this.contexts.containsKey(name)) {//子容器还不存在则进行创建this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);
}//创建子容器
//NamedContextFactory.java
protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();//这里configurations存的就是各个feign client的规范类if (this.configurations.containsKey(name)) {//获取规范类中的配置类for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {//将对应服务名称的配置类注册到该容器context.register(configuration);}}for (Map.Entry<String, C> entry : this.configurations.entrySet()) {//default开头的是全局的规范类,存的是@EnableFeignClients的defaultConfiguration属性配置的配置类if (entry.getKey().startsWith("default.")) {for (Class<?> configuration : entry.getValue().getConfiguration()) {//将全局的配置类注册到该容器context.register(configuration);}}}//注册占位符配置解析器,可以解析bean定义属性值和{@code @Value}注解中的占位符。//注册默认配置类,defaultConfigType就是FeignClientsConfiguration.classcontext.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);//添加具有最高优先级的给定属性源对象。context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object>singletonMap(this.propertyName, name)));if (this.parent != null) {// 关键!为当前容器设置父容器context.setParent(this.parent);context.setClassLoader(this.parent.getClassLoader());}context.setDisplayName(generateDisplayName(name));//刷新容器context.refresh();return context;
}

在这里插入图片描述

采用DCL锁来控制单例。

2.3 构建Feign Client实例

FeignClientFactoryBean.getObject():

//FeignClientFactoryBean.java
public Object getObject() throws Exception {return getTarget();
}//FeignClientFactoryBean.java
<T> T getTarget() {//根据spring容器,获取FeignContext,Feign的上下文,也是FeignClient的工厂类FeignContext context = this.applicationContext.getBean(FeignContext.class);//根据FeignContext,获取一个Feign的构建器//底层就是从当前feignClient名称对应的子容器中获取一些//  创建FeignClient所依赖的组件实例Feign.Builder builder = feign(context);//判断是否指定url属性,没有指定了就会负载均衡的方式进行远程调用if (!StringUtils.hasText(this.url)) {//为服务名补全协议if (!this.name.startsWith("http")) {this.url = "http://" + this.name;}else {this.url = this.name;}//拼接前缀,就是path属性,cleanPath会先格式化一下this.url += cleanPath();//没有指定url,使用具有负载均衡的远程调用客户端 构建feignClientreturn (T) loadBalance(builder, context,new HardCodedTarget<>(this.type, this.name, this.url));}//指定了url,则是直连方式if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {//补全协议this.url = "http://" + this.url;}//拼接前缀,就是path属性,cleanPath会先格式化一下String url = this.url + cleanPath();//getOptional:也是从context中对应的feignClient名称的子容器中获取Client类型的实例//这个Client就是发起远程调用的客户端Client client = getOptional(context, Client.class);if (client != null) {//判断client是否是具有负载均衡的功能client,如果是的话取消包装//确保直连if (client instanceof LoadBalancerFeignClient) {// ribbon的负载均衡客户端// not load balancing because we have a url,// but ribbon is on the classpath, so unwrap// 没有负载平衡,因为我们有一个URL,但是ribbon在类路径中,所以请取消包装client = ((LoadBalancerFeignClient) client).getDelegate();}if (client instanceof FeignBlockingLoadBalancerClient) {// openFeign的负载均衡客户端// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrap// 因为我们有一个URL,所以没有负载均衡// 但是Spring Cloud LoadBalancer在类路径上,因此请取消包装client = ((FeignBlockingLoadBalancerClient) client).getDelegate();}builder.client(client);}//从子容器获取对应类型的实例Targeter targeter = get(context, Targeter.class);//直连方式创建return (T) targeter.target(this, builder, context,new HardCodedTarget<>(this.type, this.name, url));
}

在这里插入图片描述

没有指定url,使用具有负载均衡的远程调用客户端 构建feignClient。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

//HystrixTargeter.java
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {//没有开启熔断功能话就不是熔断的Builder走这return feign.target(target);}//如果开启了熔断,就会处理一些服务降级的配置:feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName(): factory.getContextId();SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);if (setterFactory != null) {builder.setterFactory(setterFactory);}Class<?> fallback = factory.getFallback();if (fallback != void.class) {return targetWithFallback(name, context, target, builder, fallback);}Class<?> fallbackFactory = factory.getFallbackFactory();if (fallbackFactory != void.class) {return targetWithFallbackFactory(name, context, target, builder,fallbackFactory);}//也是调feign.targetreturn feign.target(target);
}
  1. 没有开启熔断功能话就不是熔断的Builder。
  2. 如果开启了熔断,就会处理一些服务降级的配置。
//Feign.java
public <T> T target(Target<T> target) {return build().newInstance(target);
}

在这里插入图片描述

ReflectiveFeign.newInstance():

//ReflectiveFeign.java
public <T> T newInstance(Target<T> target) {//targetToHandlersByName.apply:生成方法处理器//返回值nameToHandler://  key:当前feignClient的方法名//  value:方法处理器Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);//methodToHandler:key是方法对象,value是方法处理器Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();//默认方法处理器列表List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();//遍历当前feignClient的接口的所有的方法for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() == Object.class) {//Object的方法不处理continue;} else if (Util.isDefault(method)) {//是否是接口中的默认方法//默认方法创建一个默认方法处理器DefaultMethodHandler handler = new DefaultMethodHandler(method);//添加到默认方法处理器集合defaultMethodHandlers.add(handler);//保存方法和处理器映射关系methodToHandler.put(method, handler);} else {//不是默认方法,就是抽象方法//从nameToHandler获取已经生成好的对应的方法处理器methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}//jdk动态代理,创建InvocationHandler,再创建代理对象InvocationHandler handler = factory.create(target, methodToHandler);T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;
}

通过jdk动态代理, 创建InvocationHandler, 再创建代理对象。

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

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

相关文章

QT_day5:使用定时器实现闹钟

1、 程序代码&#xff1a; widget.h&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTime>//时间类 #include <QTimer>//时间事件类 #include <QTextToSpeech>//文本转语音类 QT_BEGIN_NAMESPACE namespace Ui { cla…

Python读取PDF文字转txt,解决分栏识别问题,能读两栏

搜索了一下&#xff0c;大致有这些库能将PDF转txt 1. PyPDF/PyPDF2&#xff08;截止2024.03.28这两个已经合并成了一个&#xff09;pypdf PyPI 2. pdfplumber GitHub - jsvine/pdfplumber: Plumb a PDF for detailed information about each char, rectangle, line, et cete…

R语言使用dietaryindex包计算NHANES数据多种营养指数(2)

健康饮食指数 (HEI) 是评估一组食物是否符合美国人膳食指南 (DGA) 的指标。Dietindex包提供用户友好的简化方法&#xff0c;将饮食摄入数据标准化为基于指数的饮食模式&#xff0c;从而能够评估流行病学和临床研究中对这些模式的遵守情况&#xff0c;从而促进精准营养。 该软件…

Notepad++:格式化json字符串(带转义)

目录 一、效果呈现 二、去除json字符串转义 三、格式化json字符串 一、效果呈现 格式化前 带字符串转义&#xff0c;带unicode编码字符 格式化后 二、去除json字符串转义 方法&#xff1a;采用Notepad的普通替换 第一&#xff1a;\"替换为" 第二&#xff1a;\\…

科技下乡:数字乡村改变乡村生活方式

在科技飞速发展的时代&#xff0c;数字化、信息化浪潮正以前所未有的速度席卷全球。在这场科技革命中&#xff0c;乡村不再是滞后的代名词&#xff0c;而是成为了数字乡村建设的热土。科技下乡&#xff0c;让数字乡村成为了改变乡村生活方式的重要力量。 一、科技下乡&#xf…

什么是量子计算?

什么是量子计算&#xff1f; 量子计算机仍处于起步阶段&#xff0c;正在影响已经在经典计算机上运行的新一代模拟&#xff0c;现在使用 NVIDIA cuQuantum SDK 进行加速。 在史蒂夫乔布斯 (Steve Jobs) 推出可以放入口袋的计算机之前 27 年&#xff0c;物理学家保罗贝尼奥夫 (P…

什么是JPA,JPA的概念

什么是JPA JPA&#xff08;Java Persistence API&#xff0c;Java持久化API&#xff09;&#xff0c;定义了对象-关系映射&#xff08;ORM&#xff09;以及实体对象持久化的标准接口,它是一套标准,具体的实现要根据不同的厂商来提供,就跟JDBC类型 持久化单元 持久化单元是运行…

【A-013】基于SSH的共享单车管理系统/共享单车出租系统

【A-013】基于SSH的共享单车管理系统/共享单车出租系统 开发环境&#xff1a; Eclipse/MyEclipse、Tomcat8、Jdk1.8 数据库&#xff1a; MySQL 适用于&#xff1a; 课程设计&#xff0c;毕业设计&#xff0c;学习等等 系统介绍&#xff1a; 基于SSH开发的共享单车管理系统/…

基于JavaSpringmvc+myabtis+html的鲜花商城系统设计和实现

基于JavaSpringmvcmyabtishtml的鲜花商城系统设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末…

leecode 331 |验证二叉树的前序序列化 | gdb 调试找bug

计算的本质是数据的计算 数据的计算需要采用格式化的存储&#xff0c; 规则的数据结果&#xff0c;可以快速的按照指定要求存储数据 这里就不得不说二叉树了&#xff0c;二叉树应用场景真的很多 本题讲的是&#xff0c;验证二叉树的前序序列化 换言之&#xff0c;不采用建立树的…

Redis怎么测?这篇文章写的太全了

Redis是一个高性能、内存数据库和缓存系统&#xff0c;在开发和生产环境中被广泛应用。本文将介绍如何进行有效的Redis软件测试&#xff0c;以确保其稳定性、高性能和可靠性。 Redis作为一种非关系型数据库和缓存系统&#xff0c;被广泛用于支持高流量、低延迟的应用。为了保证…

算法学习——LeetCode力扣动态规划篇9(1035. 不相交的线、53. 最大子数组和、392. 判断子序列、115. 不同的子序列)

算法学习——LeetCode力扣动态规划篇9 1035. 不相交的线 1035. 不相交的线 - 力扣&#xff08;LeetCode&#xff09; 描述 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#x…

【分析教程】unity游戏修改so文件

基础知识 0x1.apk安装后在手机中的目录 apk安装后会在两个包下生成相关包&#xff1a;data/data/、data/app/。 这里拿网易云音乐的安装目录举例。Data/App目录下通常会有三个文件&#xff1a; lib文件夹&#xff08;包含so库文件&#xff09;、 ‚oat文件夹&#xff08;O…

计算机网络-RIP动态路由协议简介

一、概述 前面我们学习了动态路由协议按照工作机制及算法划分可以分为&#xff1a;距离矢量路由协议DV型和链路状态路由协议LS型。RIP就是典型的距离矢量路由协议&#xff0c;但是实际工作中用得已经比较少了。 距离矢量路由协议DV: RIP 链路状态路由协议LS: OSPF IS-IS 二、RI…

C++11:基于C++98的语法更新

一、简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了 C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中的漏洞 进行修复&#xff0c;语言的核心部分则没有改动&#xff0c;因此人们习惯性的把两个标准合并…

【机器学习】数据探索---python主要的探索函数

在上一篇博客【机器学习】数据探索(Data Exploration)—数据质量和数据特征分析中&#xff0c;我们深入探讨了数据预处理的重要性&#xff0c;并介绍了诸如插值、数据归一化和主成分分析等关键技术。这些方法有助于我们清理数据中的噪声、消除异常值&#xff0c;以及降低数据的…

设计模式-概述篇

1. 掌握设计模式的层次 第1层&#xff1a;刚开始学编程不久&#xff0c;听说过什么是设计模式第2层&#xff1a;有很长时间的编程经验&#xff0c;自己写了很多代码&#xff0c;其中用到了设计模式&#xff0c;但是自己却不知道第3层&#xff1a;学习过了设计模式&#xff0c;…

MATLAB 自定义生成圆柱点云(49)

MATLAB 自定义生成圆柱点云(49) 一、算法介绍二、具体实现1.代码2.效果一、算法介绍 按照一些提前指定的圆柱参数,自定义生成圆柱点云,可添加噪声,用于后续的实验测试 二、具体实现 1.代码 代码如下(示例): % 指定圆柱的参数 radius = 5; % 圆柱半径 height = 20…

springcloud基本使用三(搭建nacos)

window下安装nacos: 下载页面:Releases alibaba/nacos GitHuban easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. - Releases alibaba/nacoshttps://github.com/alibaba/nacos/releases…

医药行业CRM解决方案:如何选择适合的医药CRM系统?

医药市场的竞争也同样激烈&#xff0c;抓住市场、抢占客户拼的是产品、速度&#xff0c;更是精细化的客户管理。如何抓住客户&#xff0c;并留住客户&#xff0c;是医药公司要考虑的问题。人工机械地记录数据信息很容易就被市场淘汰&#xff0c;所以医药公司也需要用数字化工具…