IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Ribbon原理分析之NamedContextFactory -> 正文阅读

[Java知识库]Ribbon原理分析之NamedContextFactory

在跟踪Ribbon源码的过程中看到了?NamedContextFactory,不懂其存在的精髓,特此记录下。

?在SpringCloud中,微服务之间由于系统的不同,可能对于远程调用来说可能需要不同的配置,比如订单系统 A 和库存系统 B,ribbon请求A,B可能需要的连接超时时间重试次数是不一致的,这个时候怎么做到ribbon请求A,B系统

的时候使用不同的配置呢。这就引入了NamedContextFactory。

从注释上来看:

它可以创建一系列的子容器,允许一系列的Specification 在每个子容器中定义自己的bean。可以参见FeignClientFactory,SpringClientFactory。就是从netflix借鉴来的。

?Ribbon使用的是SpringClientFactory。

在这个NamedContextFactory 内部还有一个内部接口:Specification

注释的意思是:使用name,configuration区分的规范。

总的来说:NamedContextFactory? 维护某个客户端使用的子容器的集合, 配置,获取容器中的bean等。

其实这个内容有点类似nacos的group,Specification用于配置分组,一个Specification实例是一个配置组,按name区分。NamedContextFactory 里面维护一系列的Specification实例中的configuration。

NamedContextFactory 中会根据这些Specification中的配置创建创建一些列的子容器,这个子容器的也按这个name分组。这样调用NamedContextFactory#getInstance的时候是带着name去获取的,就是看要获取

那个子容器内的bean。

我们先看下NamedContextFactory? 里面的一些属性。

1:propertySourceName,propertyName 构造函数传递进来赋值,会在每个子容器中生成一条配置propertySourceName=propertyName

2:?Map<String, AnnotationConfigApplicationContext> contexts 每个分组创建的子容器,都放在这个map中,key就是分组的name。就是Specification实例中的那个name。

3:Map<String, C> configurations? 存储一系列Specification实例,key就是Specification中的name。

4:ApplicationContext parent? 每个子容器的父容器,一般都是当前启动的spring项目上下文。

4:defaultConfigType 每个子容器都会使用的一个配置类。默认配置类。

1:构造函数。

第一个参数:是默认的配置类,这个配置类,是个公共的,所有的子容器都会加载它,在下面createContext方法中可以看到。

第二个参数,第三个参数:也是公共的,在每个子容器中增加一条配置属性:propertySourceName=propertyName??在下面createContext方法中可以看到。

?2:设置所有子容器的父容器,在创建子容器的时候,如果其不为空都会把这个parent设置为自己的父容器。一般这个parent都是当前的spring项目。

?3:这个是用来把创建的一系列Specification实例传递进来,用里面的configuration用来创建子容器的。

?4:? 获取所有子容器的分组name。

?5:获取某个子容器,可以被重写。

?6:创建子容器

protected AnnotationConfigApplicationContext createContext(String name) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        if (this.configurations.containsKey(name)) {
            //this.configurations.get(name) 得到的就是对应的Specification实例
         for (Class<?> configuration : this.configurations.get(name) .getConfiguration()) {
                // 输入名称为name的子容器的配置类
                context.register(configuration);
            }
        }
        for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
            // 自定义的Specification实例也可以命名为default.开头,这样每个子容器也会使用注入。
            if (entry.getKey().startsWith("default.")) {
                for (Class<?> configuration : entry.getValue().getConfiguration()) {
                    context.register(configuration);
                }
            }
        }
        // 子容器使用的默认配置类。
        context.register(PropertyPlaceholderAutoConfiguration.class,
                this.defaultConfigType);
       // 增加一个配置。
        context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
                this.propertySourceName,
                Collections.<String, Object>singletonMap(this.propertyName, name)));
        if (this.parent != null) {
            // Uses Environment from parent as well as beans
            // 子容器可以获取父容器的bean

            context.setParent(this.parent);
              // 解决java 11的问题
            // jdk11 issue
            // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
            context.setClassLoader(this.parent.getClassLoader());
        }
        context.setDisplayName(generateDisplayName(name));
        context.refresh();
        return context;
    }

7:根据name获取一个实例,先根据name获取某个子容器然后再获取bean。

	public <T> T getInstance(String name, Class<T> type) {
		AnnotationConfigApplicationContext context = getContext(name);
		if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
				type).length > 0) {
			return context.getBean(type);
		}
		return null;
	}

实践

我们自定义一套Client,和Specification来感受下这个机制。

作为客户端

public class MytestClientNamedContextFactory  extends NamedContextFactory<MytestClientSpecification> {
    public MytestClientNamedContextFactory() {
        super(BeanBaseConfig.class, "test", "myTest");
    }
}

Specification实例

public class MytestClientSpecification implements NamedContextFactory.Specification {
    private String name;

    private Class<?>[] configuration;

    public MytestClientSpecification(){

    }

    public MytestClientSpecification(String name, Class<?>[] configuration){
        this.name=name;
        this.configuration=configuration;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Class<?>[] getConfiguration() {
        return configuration;
    }
}

默认配置类
@Configuration
public class BeanBaseConfig {
    @Bean
    public TestBean testBeanCommon(){
        TestBean testBean = new TestBean();
        testBean.setBeanName("byBeanBaseConfig1");
        return testBean;
    }
}

@Configuration
public class BeanBaseConfig1 {
    @Bean
    public TestBean testBean(){
        TestBean testBean = new TestBean();
        testBean.setBeanName("byBeanBaseConfig1");
        return testBean;
    }
}

@Configuration
public class BeanBaseConfig2 {
    @Bean
    public TestBean testBean1(){
        TestBean testBean = new TestBean();
        testBean.setBeanName("byBeanBaseConfig1");
        return testBean;
    }
}

public class TestBean {
    private String beanName="testBean";

    public String getBeanName() {
        return beanName;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
}

测试:

@SpringBootTest
public class SpringRunTest {
    
    @Test
    public void test(){
        AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
        parent.register(BeanBaseConfig.class);

        parent.refresh();

        // 声明一个客户端   使用默认的配置  BeanBaseConfig
        MytestClientNamedContextFactory testClient=new MytestClientNamedContextFactory();

        // 声明客户端的 specification 其实这个specification 相当于一个nacos group 把配置分了组
        // 比如在ribbon client中 可能需要请求订单微服务和库存微服务的配置不一样  就需要到它
        MytestClientSpecification mytestClientSpecification1 = new MytestClientSpecification("specification1",new Class[]{BeanBaseConfig1.class});

        MytestClientSpecification mytestClientSpecification2 = new MytestClientSpecification("specification2",new Class[]{BeanBaseConfig1.class, BeanBaseConfig2.class});

        testClient.setConfigurations(Arrays.asList(mytestClientSpecification1,mytestClientSpecification2));
        testClient.setApplicationContext(parent);

        System.out.println(testClient.getInstances("specification1", TestBean.class));
        System.out.println("=====================================");
        System.out.println(testClient.getInstances("specification2", TestBean.class));
    }

}

结果如下:

{testBean=com.tuling.mall.user.TestBean@11c9af63, testBeanCommon=com.tuling.mall.user.TestBean@757acd7b}
=====================================
{testBean=com.tuling.mall.user.TestBean@55f616cf, testBean1=com.tuling.mall.user.TestBean@1356d4d4, testBeanCommon=com.tuling.mall.user.TestBean@c03cf28}

可以看到通过Specification实例可以起到子容器分离的效果。

Ribbon也是用这个机制。后面分析Ribbon源码的时候再提。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-23 10:43:02  更:2022-04-23 10:43:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:24:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码