【源码解析】Ribbon和Feign实现不同服务不同的配置

news2024/11/16 17:45:57

Ribbon服务实现不同服务,不同配置是通过@RibbonClientRibbonClients两个注解来实现的。@RibbonClient注册的某个Client配置类。@RibbonClients注册的全局默认配置类。

Feign实现不同服务,不同配置,是根据FeignClient来获取自定义的配置。
在这里插入图片描述

示例

定义Ribbon配置类

public class AppRibbonConfig {

    @Bean
    public 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();
    @Autowired
    private RibbonEagerLoadProperties ribbonEagerLoadProperties;

    public RibbonAutoConfiguration() {
    }

    @Bean
    public HasFeatures ribbonFeature() {
        return HasFeatures.namedFeature("Ribbon", Ribbon.class);
    }

    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }
}

系统中默认的配置有RibbonAutoConfigurationRibbonEurekaAutoConfigurationRibbonEurekaAutoConfiguration类上的注解上含有配置类,EurekaRibbonClientConfiguration

@Configuration
@EnableConfigurationProperties
@ConditionalOnRibbonAndEurekaEnabled
@AutoConfigureAfter({RibbonAutoConfiguration.class})
@RibbonClients(
    defaultConfiguration = {EurekaRibbonClientConfiguration.class}
)
public class RibbonEurekaAutoConfiguration {
    public RibbonEurekaAutoConfiguration() {
    }
}

@EnableFeignClients

该类会注册FeignClientSpecificationFeignClientsRegistrar#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() {
    }

    @Bean
    public HasFeatures feignFeature() {
        return HasFeatures.namedFeature("Feign", Feign.class);
    }

    @Bean
    public 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-provider
    enabled: 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);
        }
    }
}

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

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

相关文章

【大数据】Apache Spark 3.3.0 正式发布,新特性详解

简介 Apache Spark 3.3.0 从2021年07月03日正式开发&#xff0c;历时近一年&#xff0c;终于在2022年06月16日正式发布&#xff0c;在 Databricks Runtime 11.0 也同步发布。这个版本一共解决了 1600 个 ISSUE&#xff0c;感谢 Apache Spark 社区为 Spark 3.3 版本做出的宝贵贡…

Redis学习【8】之Redis RDB持久化

文章目录Redis 持久化1 持久化基本原理2 RDB(Redis DataBase) 持久化2.1 持久化的执行2.2 手动 save 命令2.3 手动 bgsave 命令2.4 自动条件触发2.5 查看持久化时间3 RDB 优化配置3.1 save3.2 stop-write-on-bgsave-error3.3 rdbcompression3.4 rdbchecksum3.5 sanitize-dump-p…

代码随想录算法训练营第六十三天| 503.下一个更大元素II、42. 接雨水

Leetcode - 503这题其实和下一个更大元素那题一样&#xff0c;只是说可以找自己前面的元素&#xff0c;那我索性就把遍历范围从本序列延长到两个相同序列。这样某个元素寻找下一个更大元素到本序列末尾后还可以继续往下&#xff0c;也就是从序列头开始找。这里依旧是可以用单调…

初识CSS,美化HTML

CSS称为&#xff1a;层叠样式表&#xff08;Cascading style sheets&#xff09;美化HTML即给页面种的HTML标签设置样式CSS语法规则css要写在head标签的里边&#xff0c;title标签的下面&#xff0c;用style标签框住<head> <title>...</title> <style>…

Delphi 中自定义鼠标指针图像

Dephi中的鼠标指针是可以自由定义的&#xff0c;如果是使用系统提供的图标&#xff0c;那么直接通过可视控件的Cursor属性赋值就可以。例如设置Form的鼠标为 crHourGlass 沙漏&#xff1a;Form1.Cursor : crHourGlass;也可以在设计期&#xff08;IDE环境中&#xff09;直接更改…

几行代码,就写完懒加载啦?

Ⅰ、前言 「懒加载」是网页中非常 常见的&#xff1b;为了减少系统的压力&#xff0c;对于一些电商系统出场频率非常高&#xff1b;那么大家一般用什么方式去实现 「懒加载」 呢 &#xff1f; ① 通过 scroll 的形式&#xff1a; 通过 滚动「scroll」事件&#xff0c;然后去判…

关系抽取概述

关系抽取介绍 关系抽取概念字1988年在MUL大会上提出&#xff0c;是信息抽取的基本任务之一&#xff0c;目的是为了识别出文本实体中的目标关系。 知识图是语义关联的实体&#xff0c;它将人们对物理世界的认知转化为计算机能够义结构化方式理解的语义信息。 关系抽取通过识别实…

LeetCode分类刷题----二叉树

二叉树1.二叉树的递归遍历144.二叉树的前序遍历145.二叉树的后序遍历94.二叉树的中序遍历2.二叉树的迭代遍历144.二叉树的前序遍历145.二叉树的后序遍历94.二叉树的中序遍历3.二叉树的层序遍历102.二叉树的层序遍历107.二叉树的层序遍历||199.二叉树的右视图637.二叉树的层平均…

数据驱动的两阶段分布鲁棒(1-范数和∞-范数约束)的电热综合能源系统研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

笔记本加装固态和内存条教程(超详细)

由于笔记本是几年前买的了&#xff0c;当时是4000&#xff0c;现在用起来感到卡顿&#xff0c;启动、运行速度特别慢&#xff0c;就决定换个固态硬盘&#xff0c;加个内存条&#xff0c;再给笔记本续命几年。先说一下加固态硬盘SSD的好处&#xff1a;1.启动快 2.读取延迟小 3.写…

ABB机器人将实时坐标发送给西门子PLC的具体方法示例

ABB机器人将实时坐标发送给西门子PLC的具体方法示例 本次以PROFINET通信为例进行说明,演示ABB机器人将实时坐标发送给西门子PLC的具体方法。 首先,要保证ABB机器人和PLC的信号地址分配已经完成,具体的内容可参考以下链接: S7-1200PLC与ABB机器人进行PROFINET通信的具体方法…

企鹅号是什么,微信视频号和微视的区别

目录 企鹅号是什么 微视是什么 微信视频号是什么 微信视频号和微视的区别 企鹅号是什么 企鹅号是腾讯旗下的一站式内容&#xff08;文本&#xff09;创作运营平台&#xff0c;致力于帮助媒体、自媒体、企业、机构获得更多曝光与关注&#xff0c;、 持续扩大品牌影响力和商…

他是00年的,我们卷不过他...

现在的小年轻真的卷得过分了。前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家里条…

Win10耳机有声音麦不能说话怎么办?麦克风说话别人听不到解决方法

网上找了一些解决办法&#xff0c;一般都是重复的&#xff0c;几个设置调来调去也就那样&#xff0c;没什么用 这种问题一般是“老式”一点的台式机会出现&#xff0c;提供的解决办法如下&#xff1a; 首先下载带面板的音频管理器&#xff0c;如realtek高清晰音频管理器&…

分享88个HTML旅游交通模板,总有一款适合您

88个HTML旅游交通模板下载链接&#xff1a;https://pan.baidu.com/s/1pziNhgpC53h3KZy_a-aAFQ?pwdf99e 提取码&#xff1a;f99e Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 HTML5旅行公司旅行社网站模板 HTML5旅行公司旅行社网站模板是一款提供旅行服务的公司…

数据分析spss应急考试

数据分析spss应急考试 前言 单项选择 15(项)*2&#xff08;分&#xff09;30 判断题 10*1 10 计算题 2*10 案例分析题目(考实验内容) 总四十分&#xff0c;分值不等 老师重点强调了回归分析因子分析方差分析参数、非参数检验 2独立样本的非参数检验应该用什么方法多独立样本…

Qt 开发使用VSCode 笔记2

在之前有写过使用VSCode开发QT的笔记 Qt 开发使用VSCode 在以前的基础上继续学习记录写下《Qt 开发使用VSCode 笔记2》 该笔记相比之前的Qt 开发使用VSCode新加了如下内容&#xff1a; 工作区的使用使用Natvis进行Qt感知对象可视化通过vscode创建QT Quick项目 工作区的使用 …

jsp诊疗预约系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp诊疗预约系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;使用jav…

Java创建对象的方式

Java创建对象的五种方式&#xff1a; &#xff08;1&#xff09;使用new关键字 &#xff08;2&#xff09;使用Object类的clone方法 &#xff08;3&#xff09;使用Class类的newInstance方法 &#xff08;4&#xff09;使用Constructor类中的newInstance方法 &#xff08;5&am…

[MySQL]初识数据库

哈喽&#xff0c;大家好&#xff01;我是保护小周ღ&#xff0c;本期为大家带来的是 MySQL 数据库&#xff0c;也是新的知识&#xff0c;首先我们会初步认识什么是数据库&#xff0c;什么是Mysql 数据库&#xff0c;以及我们 mysql 主要学什么&#xff0c;SQL 语句简单使用&…