[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.mzph.cn/news/781106.shtml

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

相关文章

HarmonyOS实战开发-如何实现一个支持加减乘除混合运算的计算器。

介绍 本篇Codelab基于基础组件、容器组件&#xff0c;实现一个支持加减乘除混合运算的计算器。 说明&#xff1a; 由于数字都是双精度浮点数&#xff0c;在计算机中是二进制存储数据的&#xff0c;因此小数和非安全整数&#xff08;超过整数的安全范围[-Math.pow(2, 53)&#…

【详解】运算放大器工作原理及其在信号处理中的核心作用

什么是运算放大器 运算放大器&#xff08;简称“运放”&#xff09;是一种放大倍数非常高的电路单元。在实际电路中&#xff0c;它常常与反馈网络一起组成一定的功能模块。它是一种带有特殊耦合电路和反馈的放大器。输出信号可以是输入信号的加法、减法、微分和积分等数学运算…

vue3+ts项目 | axios 的测试 | 测试接口

在 App.vue 中&#xff0c;测试接口 // 测试接口import request from /utils/request;import { onMounted } from vue;onMounted(() > {request.get(/hosp/hospital/1/10).then((res) > {console.log("APP组件展示获取的数据",res);})}) 在request.ts中&…

link 样式表是否会阻塞页面内容的展示?取决于浏览器,edge 和 chrome 会,但 firefox 不会。

经过实测&#xff1a; 在 head 中 link 一个 1M 大小的样式表。设置网络下载时间大概为 10 秒。 edge 和 chrome 只有在下载完样式表后&#xff0c;页面上才会出现内容。而 firefox 可以直接先显示内容&#xff0c;然后等待样式表下载完成后再应用样式。 DOMContentLoaded 事…

Vue指令之v-for

跟其他语言中的for一样&#xff0c;是用来渲染多个类似实例的。 语法为v-for"(item, index) in 可迭代对象"&#xff0c;一般就用于遍历数组。这里的语法跟Python中的for循环enumerate也有点相似之处&#xff0c;但要注意item在前&#xff0c;index在后&#xff0c;…

复杂度3 二分查找函数

文章预览&#xff1a; 题目算法代码 题目 算法 本题要求用二分法查找顺序表的一个值&#xff0c;比较简单注意指针格式即可 代码 Position BinarySearch( List L, ElementType X ) {int begin1,mid;int endL->Last;ElementType temp;while(begin<end){mid(beginend)/2…

【Spring Security】 快速入门

文章目录 一、 身份认证Demo1、创建工程2、代码编写2.1、Controller2.2、Html2.3、application.properties配置 3、启动项目并访问 二、Spring Security 默认做了什么二、底层原理1.概述2.FiltersDelegatingFilterProxyFilterChainProxySecurityFilterChainSecurity Filters 三…

vue3-pinia使用(末尾有彩蛋)

什么是 pinia Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。 之前用的是 vuex&#xff0c;后面 vue 官方团队不维护了&#xff0c;推荐使用 pinia 安装 yarn add pinia # 或者使用 npm npm install piniapnpm install piniaStore 是什么&#xf…

实验一 Python集成开发环境的搭建及可视化库的安装

一、安装集成开发环境 下载安装包 官方网址&#xff1a; Free Download | Anaconda 或者镜像网站下载&#xff08;较快&#xff09; https://repo.anaconda.com/archive/ 安装 配置环境变量 验证 输入&#xff1a; conda -V 二、下载pyecharts环境 点击 Anaconda Promp…

spring-boot之shiro安全框架配置使用

shiro架构&#xff08;外部&#xff09; shiro架构(内部) 具体API操作 获取当前的用户对象 Subject currentUser SecurityUtils.getSubject();通过当前用户拿到session Session session currentUser.getSession(); session.setAttribute("someKey", "aValu…

微信公众号运营必备工具合集

微信公众号运营必备工具合集 各位同学&#xff0c;想要成为一名合格的公众号运营&#xff0c;必须要搭建一个属于自己的运营工具库&#xff0c;可以在日常工作中最大限度的提高效率。 91微信编辑器 &#xff1a;http://bj.91join.com/ 壹伴助手&#xff1a;https://yiban.io…

下载及安装PHP,composer,phpstudy,thinkPHP6.0框架

文章目录 前言 thinkPHP是一款开源的PHP框架&#xff0c;它是基于MVC&#xff08;Model-View-Controller&#xff09;设计模式构建的。thinkPHP提供了丰富的功能和组件&#xff0c;使得开发人员可以快速、高效地构建和维护Web应用程序。 以下是thinkPHP框架的一些特点和功能&…

上位机图像处理和嵌入式模块部署(qmacvisual测量标定)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 在机器视觉中,测量是很重要的一个环节。如果是简单的定位,可能精度要求并不那么严格。但是如果是对产品进行QA测量,需要精确到0.1mm,甚至是0.05mm这样的精度,那就需要对camera…

蓝队面经(一)

蓝队面经(一) 文章目录 蓝队面经(一)入侵排查思路windows入侵排查思路Linux入侵排查思路 Linux 如何查看登录日志Windows 和 Linux 的日志文件放在哪里&#xff1f;WindowsLinux Linux 常用排查命令有哪些&#xff1f;Linux 的 Selinux 是什么&#xff1f;如何设置 Selinux&…

【Leetcode】2952. 需要添加的硬币的最小数量

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果总结 题目 题目链接&#x1f517; 给你一个下标从 0 0 0 开始的整数数组 c o i n s coins coins&#xff0c;表示可用的硬币的面值&#xff0c;以及一个整数 t a r g e t target target 。 如果存在某个 c o i …

剑指Offer题目笔记21(计数排序)

面试题74&#xff1a; 问题&#xff1a; ​ 输入一个区间的集合&#xff0c;将重叠的区间合并。 解决方案&#xff1a; ​ 先将所有区间按照起始位置排序&#xff0c;然后比较相邻两个区间的结束位置就能知道它们是否重叠。如果它们重叠就将它们合并&#xff0c;然后判断合并…

精选2024年最佳项目管理系统!实用推荐与详细评测

随着企业规模的扩大&#xff0c;项目量也会呈几何倍的增长&#xff0c;项目管理系统就成了企业管理必不可少的一部分。2024年优秀的项目管理系统推荐。今年为大家带来Microsoft Project、Zoho Projects、Jira以及Wrike项目管理系统评测。 什么是项目管理系统&#xff1f; 项目…

怎么更新sd-webui AUTOMATIC1111/stable-diffusion-webui ?

整个工程依靠脚本起来的&#xff1a; 可直接到stable-diffusion-webui子目录执行&#xff1a; git pull更新代码完毕后&#xff0c;删除venv的虚拟环境。 然后再次执行webui.sh&#xff0c;这样会自动重新启动stable-diffusion-webui.

springboot心灵治愈交流平台

摘 要 本论文主要论述了如何使用JAVA语言开发一个心灵治愈交流平台 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述心灵治愈交流平台的当前背景以及系统开发的…

啥是MCU,MCU科普

啥是MCU&#xff0c;MCU科普 附赠自动驾驶学习资料和量产经验&#xff1a;链接 MCU是Microcontroller Unit 的简称&#xff0c;中文叫微控制器&#xff0c;俗称单片机&#xff0c;是把CPU的频率与规格做适当缩减&#xff0c;并将内存、计数器、USB、A/D转换、UART、PLC、DMA等…