[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://xiahunao.cn/news/2905518.html

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

相关文章

【计算机网络篇】数据链路层(4.2)可靠传输的实现机制

文章目录 &#x1f354;可靠传输的实现机制⭐停止 - 等待协议&#x1f5d2;️注意 &#x1f50e;停止 - 等待协议的信道利用率&#x1f5c3;️练习题 ⭐回退N帧协议&#x1f388;回退N帧协议的基本工作流程&#x1f50e;无传输差错的情况&#x1f50e;超时重传的情况&#x1f5…

【IC前端虚拟项目】write_path子模块DS与RTL编码

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 read_path的代码完成之后,就可以开始整个项目里复杂度最高、bug最多、时序收敛最为困难的模块——write_path的开发了!我自己写过两次这个虚拟项目,每次都是在这里耗时最久,所以大家也可以挑战一下自…

容器三(ArrayList、LinkedList、Vector)

目录 ArrayList 特点和底层实现 LinkedList 特点和底层实现 Vector 向量 ArrayList 特点和底层实现 ArrayList 底层是用数组实现的存储。 特点&#xff1a;查询效率高&#xff0c;增删效率低&#xff0c;线程不安全。 在 List 的多个实现类中&#xff0c;我们一般使用它来处理…

【机器学习】数据探索(Data Exploration)---数据质量和数据特征分析

一、引言 在机器学习项目中&#xff0c;数据探索是至关重要的一步。它不仅是模型构建的基础&#xff0c;还是确保模型性能稳定、预测准确的关键。数据探索的过程中&#xff0c;数据质量和数据特征分析占据了核心地位。数据质量直接关系到模型能否从数据中提取有效信息&#xff…

【公示】2023年度青岛市级科技企业孵化器拟认定名单

根据《青岛市科技企业孵化器管理办法》&#xff08;青科规〔2023〕1号&#xff09;&#xff08;以下简称《管理办法》&#xff09;、《关于开展2023年度市级科技企业孵化器认定申报工作的通知》&#xff0c;经申报受理、区市推荐、形式审查、专家评审及现场核查等程序&#xff…

leetcode热题100.柱状图中最大的矩形

Problem: 84. 柱状图中最大的矩形 文章目录 题目思路复杂度Code 题目 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;hei…

牛客小白月赛89(A,B,C,D,E,F)

比赛链接 官方视频讲解&#xff08;个人觉得讲的还是不错的&#xff09; 这把BC偏难&#xff0c;差点就不想做了&#xff0c;对小白杀伤力比较大。后面的题还算正常点。 A 伊甸之花 思路&#xff1a; 发现如果这个序列中最大值不为 k k k&#xff0c;我们可以把序列所有数…

【QT学习】2.信号与槽,标准信号与槽,自建信号,自建槽

1.主函数的进一步解释 2.信号与槽的简单例子 connect(&but1, //发送者&QPushButton::pressed, //发送触发事件this, //接受者&MainWindow::close); //接受数据后处理操作 connect&#xff08;&#xff09; 参数1&#xf…

unity学习(76)--窗口化和后台运行

1.通过如下方式将编译的游戏设置为窗口模式。 成功&#xff1a; 2.现在只有鼠标点击的窗体游戏运动&#xff0c;其他窗体游戏都会卡住。 2.1build setting中 2.2unity内部Project Settings 也被同步修改了

深度学习故障诊断实战 | 数据预处理之创建Dataloader数据集

前言 本期给大家分享介绍如何用Dataloader创建数据集 背景 示例代码 from torch import nn import torch import os import numpy as np import pandas as pd import matplotlib.pyplot as plt import time import torch.functional as F from sklearn.manifold import TSNE…

【漏洞复现】大华综合安防监控管理平台 Digital Surveillance System系统存在RCE漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

《亮数据:爬虫数据采集行业痛点的利器》

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

npm救赎之道:探索--save与--save--dev的神秘力量!

目录 1. --save和--save-dev是什么&#xff1f;2. 区别与应用场景--save--save-dev 3. 生产环境与开发环境4. 实际应用示例--save--save-dev 5. 总结 在现代软件开发中&#xff0c;npm&#xff08;Node Package Manager&#xff09;扮演着不可或缺的角色&#xff0c;为开发者提…

第二十一章 Jquery ajax

文章目录 1. jquery下载2. jquery的使用3. jquery页面加载完毕执行4. jquery属性控制6. 遍历器 2. ajax1. 准备后台服务器2. ajax发送get请求3. ajax发送post请求 1. jquery下载 点击下载 稳定版本1.9 2. jquery的使用 存放到html文件的同级目录 3. jquery页面加载完毕执行…

Colorize (Texture Color Palette Modifier)

Colorize提供了无与伦比的区域颜色调整和效果控制,如使用纹理调色板的模型的发射、金属反射和模拟金属遮挡。 Colorize彻底改变了你在Unity中为3D模型添加颜色和生命的方式。无论你是一个独立开发者、艺术家,还是一个大型团队的一员,Colorize都提供了一套直观、强大的工具,…

Tomcat配置https

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、证书 CA证书申请下载不赘述了。 二、上传证书 进入tomcat根目录&#xff0c;conf同级目录下创建cert文件夹&#xff0c;并将证书两个文件上传到该文件夹&#xff1b; 三、编辑conf/server.xml文件 ① …

3723. 字符串查询:做题笔记

目录 思路 代码 注意点 3723. 字符串查询 思路 这道题感觉和常见的前缀和问题不太一样&#xff0c;前缀和的另一种应用&#xff1a;可以统计次数。 这道题我们想判断一个单词的其中一段子序列A是否可以通过重新排列得到另一段子序列B。 我看到这道题的时候想着可能要判…

华为Mate60RS非凡大师和华为Mate50RS保时捷对比

外观设计&#xff1a;Mate 60RS采用了更加高端的材质和工艺&#xff0c;相比Mate 50RS更加坚固耐用&#xff0c;同时具备更多的细节设计&#xff0c;如更加精致的纹理和镀铬边框等。 屏幕显示&#xff1a;Mate 60RS的屏幕分辨率更高&#xff0c;达到了32001440像素&#xff0c…

Excel·VBA数组分组问题

看到一个帖子《excel吧-数据分组问题》&#xff0c;对一组数据分成4组&#xff0c;使每组的和值相近 目录 代码思路1&#xff0c;分组形式、可分组数代码1代码2代码2举例 2&#xff0c;数组所有分组形式举例 这个问题可以转化为2步&#xff1a;第1步&#xff0c;获取一组数据…

线程的状态:操作系统层面和JVM层面

在操作系统层面&#xff0c;线程有五种状态 初始状态&#xff1a;线程被创建&#xff0c;操作系统为其分配资源。 可运行状态(就绪状态)&#xff1a;线程被创建完成&#xff0c;进入就绪队列&#xff0c;参与CPU执行权的争夺。或因为一些原因&#xff0c;从阻塞状态唤醒的线程…