客户管理系统开发定制Ribbon原理分析之NamedContextFactory

客户管理系统开发定制在跟踪源码的过程中看到了 NamedContextFactory,客户管理系统开发定制不懂其存在的精髓,客户管理系统开发定制特此记录下。

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

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

从注释上来看:

它可以创建一系列的子容器,允许一系列的 在每个子容器中定义自己的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:创建子容器

  1. protected AnnotationConfigApplicationContext createContext(String name) {
  2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  3. if (this.configurations.containsKey(name)) {
  4. //this.configurations.get(name) 得到的就是对应的Specification实例
  5. for (Class<?> configuration : this.configurations.get(name) .getConfiguration()) {
  6. // 输入名称为name的子容器的配置类
  7. context.register(configuration);
  8. }
  9. }
  10. for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
  11. // 自定义的Specification实例也可以命名为default.开头,这样每个子容器也会使用注入。
  12. if (entry.getKey().startsWith("default.")) {
  13. for (Class<?> configuration : entry.getValue().getConfiguration()) {
  14. context.register(configuration);
  15. }
  16. }
  17. }
  18. // 子容器使用的默认配置类。
  19. context.register(PropertyPlaceholderAutoConfiguration.class,
  20. this.defaultConfigType);
  21. // 增加一个配置。
  22. context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
  23. this.propertySourceName,
  24. Collections.<String, Object>singletonMap(this.propertyName, name)));
  25. if (this.parent != null) {
  26. // Uses Environment from parent as well as beans
  27. // 子容器可以获取父容器的bean
  28. context.setParent(this.parent);
  29. // 解决java 11的问题
  30. // jdk11 issue
  31. // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
  32. context.setClassLoader(this.parent.getClassLoader());
  33. }
  34. context.setDisplayName(generateDisplayName(name));
  35. context.refresh();
  36. return context;
  37. }

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

  1. public <T> T getInstance(String name, Class<T> type) {
  2. AnnotationConfigApplicationContext context = getContext(name);
  3. if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
  4. type).length > 0) {
  5. return context.getBean(type);
  6. }
  7. return null;
  8. }

实践

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

  1. 作为客户端
  2. public class MytestClientNamedContextFactory extends NamedContextFactory<MytestClientSpecification> {
  3. public MytestClientNamedContextFactory() {
  4. super(BeanBaseConfig.class, "test", "myTest");
  5. }
  6. }
  7. Specification实例
  8. public class MytestClientSpecification implements NamedContextFactory.Specification {
  9. private String name;
  10. private Class<?>[] configuration;
  11. public MytestClientSpecification(){
  12. }
  13. public MytestClientSpecification(String name, Class<?>[] configuration){
  14. this.name=name;
  15. this.configuration=configuration;
  16. }
  17. @Override
  18. public String getName() {
  19. return name;
  20. }
  21. @Override
  22. public Class<?>[] getConfiguration() {
  23. return configuration;
  24. }
  25. }
  26. 默认配置类
  27. @Configuration
  28. public class BeanBaseConfig {
  29. @Bean
  30. public TestBean testBeanCommon(){
  31. TestBean testBean = new TestBean();
  32. testBean.setBeanName("byBeanBaseConfig1");
  33. return testBean;
  34. }
  35. }
  36. @Configuration
  37. public class BeanBaseConfig1 {
  38. @Bean
  39. public TestBean testBean(){
  40. TestBean testBean = new TestBean();
  41. testBean.setBeanName("byBeanBaseConfig1");
  42. return testBean;
  43. }
  44. }
  45. @Configuration
  46. public class BeanBaseConfig2 {
  47. @Bean
  48. public TestBean testBean1(){
  49. TestBean testBean = new TestBean();
  50. testBean.setBeanName("byBeanBaseConfig1");
  51. return testBean;
  52. }
  53. }
  54. public class TestBean {
  55. private String beanName="testBean";
  56. public String getBeanName() {
  57. return beanName;
  58. }
  59. public void setBeanName(String beanName) {
  60. this.beanName = beanName;
  61. }
  62. }

测试:

  1. @SpringBootTest
  2. public class SpringRunTest {
  3. @Test
  4. public void test(){
  5. AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
  6. parent.register(BeanBaseConfig.class);
  7. parent.refresh();
  8. // 声明一个客户端 使用默认的配置 BeanBaseConfig
  9. MytestClientNamedContextFactory testClient=new MytestClientNamedContextFactory();
  10. // 声明客户端的 specification 其实这个specification 相当于一个nacos group 把配置分了组
  11. // 比如在ribbon client中 可能需要请求订单微服务和库存微服务的配置不一样 就需要到它
  12. MytestClientSpecification mytestClientSpecification1 = new MytestClientSpecification("specification1",new Class[]{BeanBaseConfig1.class});
  13. MytestClientSpecification mytestClientSpecification2 = new MytestClientSpecification("specification2",new Class[]{BeanBaseConfig1.class, BeanBaseConfig2.class});
  14. testClient.setConfigurations(Arrays.asList(mytestClientSpecification1,mytestClientSpecification2));
  15. testClient.setApplicationContext(parent);
  16. System.out.println(testClient.getInstances("specification1", TestBean.class));
  17. System.out.println("=====================================");
  18. System.out.println(testClient.getInstances("specification2", TestBean.class));
  19. }
  20. }

结果如下:

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

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

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

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发