【源码解析】Ribbon和Feign实现不同服务不同的配置
Ribbon服务实现不同服务,不同配置是通过@RibbonClient
和RibbonClients
两个注解来实现的。@RibbonClient
注册的某个Client配置类。@RibbonClients
注册的全局默认配置类。
Feign实现不同服务,不同配置,是根据FeignClient
来获取自定义的配置。
示例
定义Ribbon配置类
public class AppRibbonConfig {@Beanpublic IPing iping() {return new DummyPing();}
}
启动类上添加注解
@RibbonClient(name = "app-provider", configuration = AppRibbonConfig.class)
源码解析
@RibbonClient
和@RibbonClients
这两个注解的功能都是引入RibbonClientConfigurationRegistrar
,该类主要是生成RibbonClientSpecification
的BeanDefinition。
RibbonAutoConfiguration
该类会获取到所有的RibbonClientSpecification
,设置到SpringClientFactory
的map集合中。
public class RibbonAutoConfiguration {@Autowired(required = false)private List<RibbonClientSpecification> configurations = new ArrayList();@Autowiredprivate RibbonEagerLoadProperties ribbonEagerLoadProperties;public RibbonAutoConfiguration() {}@Beanpublic HasFeatures ribbonFeature() {return HasFeatures.namedFeature("Ribbon", Ribbon.class);}@Beanpublic SpringClientFactory springClientFactory() {SpringClientFactory factory = new SpringClientFactory();factory.setConfigurations(this.configurations);return factory;}
}
系统中默认的配置有RibbonAutoConfiguration
和RibbonEurekaAutoConfiguration
,RibbonEurekaAutoConfiguration
类上的注解上含有配置类,EurekaRibbonClientConfiguration
。
@Configuration
@EnableConfigurationProperties
@ConditionalOnRibbonAndEurekaEnabled
@AutoConfigureAfter({RibbonAutoConfiguration.class})
@RibbonClients(defaultConfiguration = {EurekaRibbonClientConfiguration.class}
)
public class RibbonEurekaAutoConfiguration {public RibbonEurekaAutoConfiguration() {}
}
@EnableFeignClients
该类会注册FeignClientSpecification
。FeignClientsRegistrar#registerBeanDefinitions
,会加载默认配置和对应的FeignClient
服务名的配置。
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {this.registerDefaultConfiguration(metadata, registry);this.registerFeignClients(metadata, registry);}private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {String name;if (metadata.hasEnclosingClass()) {name = "default." + metadata.getEnclosingClassName();} else {name = "default." + metadata.getClassName();}this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));}}private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());}
FeignAutoConfiguration
FeignAutoConfiguration
会获取到FeignClientSpecification
@Configuration
@ConditionalOnClass({Feign.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {@Autowired(required = false)private List<FeignClientSpecification> configurations = new ArrayList();public FeignAutoConfiguration() {}@Beanpublic HasFeatures feignFeature() {return HasFeatures.namedFeature("Feign", Feign.class);}@Beanpublic FeignContext feignContext() {FeignContext context = new FeignContext();context.setConfigurations(this.configurations);return context;}
FeignClientFactoryBean
系统启动的时候,会加载FeignClientFactoryBean
,会执行FeignClientFactoryBean#getObject
,获取到context对象,从而获取相应的组件。
protected Builder feign(FeignContext context) {FeignLoggerFactory loggerFactory = (FeignLoggerFactory)this.get(context, FeignLoggerFactory.class);Logger logger = loggerFactory.create(this.type);Builder builder = ((Builder)this.get(context, Builder.class)).logger(logger).encoder((Encoder)this.get(context, Encoder.class)).decoder((Decoder)this.get(context, Decoder.class)).contract((Contract)this.get(context, Contract.class));this.configureFeign(context, builder);return builder;}protected <T> T get(FeignContext context, Class<T> type) {T instance = context.getInstance(this.contextId, type);if (instance == null) {throw new IllegalStateException("No bean found of type " + type + " for " + this.contextId);} else {return instance;}}
第一次访问服务
Ribbon是懒加载的,NamedContextFactory#getContext
,第一次访问是没有context对象的,所以会进行创建。
protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized(this.contexts) {if (!this.contexts.containsKey(name)) {this.contexts.put(name, this.createContext(name));}}}return (AnnotationConfigApplicationContext)this.contexts.get(name);}
NamedContextFactory#createContext
,根据服务配置生成对应的AnnotationConfigApplicationContext
protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();if (this.configurations.containsKey(name)) {Class[] var3 = ((NamedContextFactory.Specification)this.configurations.get(name)).getConfiguration();int var4 = var3.length;for(int var5 = 0; var5 < var4; ++var5) {Class<?> configuration = var3[var5];context.register(new Class[]{configuration});}}Iterator var9 = this.configurations.entrySet().iterator();while(true) {Entry entry;do {if (!var9.hasNext()) {context.register(new Class[]{PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType});context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, name)));if (this.parent != null) {context.setParent(this.parent);context.setClassLoader(this.parent.getClassLoader());}context.setDisplayName(this.generateDisplayName(name));context.refresh();return context;}entry = (Entry)var9.next();} while(!((String)entry.getKey()).startsWith("default."));Class[] var11 = ((NamedContextFactory.Specification)entry.getValue()).getConfiguration();int var12 = var11.length;for(int var7 = 0; var7 < var12; ++var7) {Class<?> configuration = var11[var7];context.register(new Class[]{configuration});}}}
Ribbon提前加载
ribbon:eager-load:clients: app-providerenabled: true
在RibbonAutoConfiguration
中会根据配置判断是否生成对象ribbonApplicationContextInitializer
@Bean
@ConditionalOnProperty({"ribbon.eager-load.enabled"})
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {return new RibbonApplicationContextInitializer(this.springClientFactory(), this.ribbonEagerLoadProperties.getClients());
}
RibbonApplicationContextInitializer#initialize
,该类主要是根据配置的clients数据对相应的服务进行加载
protected void initialize() {if (this.clientNames != null) {Iterator var1 = this.clientNames.iterator();while(var1.hasNext()) {String clientName = (String)var1.next();this.springClientFactory.getContext(clientName);}}
}
相关文章:

【源码解析】Ribbon和Feign实现不同服务不同的配置
Ribbon服务实现不同服务,不同配置是通过RibbonClient和RibbonClients两个注解来实现的。RibbonClient注册的某个Client配置类。RibbonClients注册的全局默认配置类。 Feign实现不同服务,不同配置,是根据FeignClient来获取自定义的配置。 示…...

【webpack5】一些常见优化配置及原理介绍(二)
这里写目录标题介绍sourcemap定位报错热模块替换(或热替换,HMR)oneOf精准解析指定或排除编译开启缓存多进程打包移除未引用代码配置babel,减小代码体积代码分割(Code Split)介绍预获取/预加载(prefetch/pre…...

力扣sql简单篇练习(十九)
力扣sql简单篇练习(十九) 1 查询结果的质量和占比 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 用count是不会统计为null的数据的 SELECT query_name,ROUND(AVG(rating/position),2) quality,ROUND(count(IF(rating<3,rating,null))/count(r…...
线段树c++
前言 在谈论到种种算法知识与数据结构的时候,线段树无疑总是与“简单”和“平常”联系起来的。而这些特征意味着,线段树作为一种常用的数据结构,有常用性,基础性和易用性等诸多特点。因此,今天我来讲一讲关于线段树的话题。 定义 首先,线段树是一棵“树”,而且是一棵…...

HTML+CSS+JavaScript学习笔记~ 从入门到精通!
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、HTML1. 什么是HTML?一个完整的页面:<!DOCTYPE> 声明中文编码2.HTML基础①标签头部元素标题段落注释水平线文本格式化②属性3.H…...

LeetCode 430. 扁平化多级双向链表
原题链接 难度:middle\color{orange}{middle}middle 题目描述 你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一…...

2.5|iot|第1章嵌入式系统概论|操作系统概述|嵌入式操作系统
目录 第1章: 嵌入式系统概论 1.嵌入式系统发展史 2.嵌入式系统定义* 3.嵌入式系统特点* 4.嵌入式处理器的特点 5.嵌入式处理分类 6.嵌入式系统的应用领域及嵌入式系统的发展趋势 第8章:Linux内核配置 1.内核概述 2.内核代码结构 第1章…...
一文教会你使用ChatGPT画图
引言 当今,ChatGPT在各行各业都有着广泛的应用,其自然语言处理技术也日益成熟。ChatGPT是一种被广泛使用的技术,除了能够生成文本,ChatGPT还可以用于绘图,这为绘图技术的学习和应用带来了新的可能性。本文将介绍如何利用ChatGPT轻松绘制各种形状,为对绘图技术感兴趣的读…...
Java资料分享
随着Java开发的薪资越来越高,越来越多人开始学习 Java 。在众多编程语言中,Java学习难度还是偏高的,逻辑性也比较强,但是为什么还有那么多人要学Java呢?Java语言是目前流行的互联网等企业的开发语言,是市面…...

yum/vim工具的使用
yum 我们生活在互联网发达的时代,手机电脑也成为了我们生活的必须品,在你的脑海中是否有着这样的记忆碎片,在一个明媚的早上你下定决心准备发奋学习,“卸载”了你手机上的所有娱乐软件,一心向学!可是到了下…...

内网渗透(三十九)之横向移动篇-pass the ticket 票据传递攻击(PTT)横向攻击
系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…...
Unity性能优化之纹理格式终极篇
知识早班车:1、当n大于1时,2的n次幂一定能被4整除;证明:2^n 2^2*2^(n-1) 4*2^(n-1)2、4的倍数不一定都是2的次幂;证明:4*3 12;12不是2的次幂3、Pixel(像素)是组成图片…...

【Spark分布式内存计算框架——Spark SQL】9. Dataset(下)RDD、DF与DS转换与面试题
5.3 RDD、DF与DS转换 实际项目开发中,常常需要对RDD、DataFrame及Dataset之间相互转换,其中要点就是Schema约束结构信息。 1)、RDD转换DataFrame或者Dataset 转换DataFrame时,定义Schema信息,两种方式转换为Dataset时…...

Windows 环境下,cmake工程导入OpenCV库
目录 1、下载 OpenCV 库 2、配置环境变量 3、CmakeLists.txt 配置 1、下载 OpenCV 库 OpenCV官方下载地址:download | OpenCV 4.6.0 下载完毕后解压,便可以得到下面的文件 2、配置环境变量 我们需要添加两个环境变量,一个是 OpenCVConfi…...
微服务架构设计模式-(16)重构
绞杀者应用程序 由微服务组成的应用程序,将新功能作为服务,并逐步从单体应用中提取服务来实现。好处 尽早并频繁的体现价值 快速开发交付,使用 与之相对的是“一步到位”重构,这时间长,且期间有新的功能加入ÿ…...

数据结构:归并排序和堆排序
归并排序 归并排序(merge sort)是利用“归并”操作的一种排序方法。从有序表的讨论中得知,将两个有序表“归并”为一个有序表,无论是顺序表还是链表,归并操作都可以在线性时间复杂度内实现。归并排序的基本操作是将两个位置相邻的有序记录子序列R[i…m]R[m1…n]归并为一个有序…...

基于easyexcel的MySQL百万级别数据的excel导出功能
前言最近我做过一个MySQL百万级别数据的excel导出功能,已经正常上线使用了。这个功能挺有意思的,里面需要注意的细节还真不少,现在拿出来跟大家分享一下,希望对你会有所帮助。原始需求:用户在UI界面上点击全部导出按钮…...
js-DOM02
1.DOM查询 - 通过具体的元素节点来查询 - 元素.getElementsByTagName() - 通过标签名查询当前元素的指定后代元素 - 元素.childNodes - 获取当前元素的所有子节点 - 会获取到空白的文本子节点 …...

作为一名开发工程师,我对 ChatGPT 的一些看法
ChatGPT 又又火了。 ChatGPT 第一次爆火是2022年12月的时候,我从一些球友的讨论中知道了这个 AI 程序。 今年2月,ChatGPT 的热火更加猛烈,这时我才意识到,原来上次的热火只是我们互联网圈子内部火了,这次是真真正正的破圈了,为大众所熟悉了。 这个 AI 程序是一个智能问…...
Flask中基于Token的身份认证
Flask提供了多种身份认证方式,其中基于Token的身份认证是其中一种常用方式。基于Token的身份认证通常是在用户登录之后,为用户生成一个Token,然后在每次请求时用户将该Token作为请求头部中的一个参数进行传递,服务器端在接收到请求…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...