1. 程式人生 > >dubbo系列之spring boot核心配置讀取(三)

dubbo系列之spring boot核心配置讀取(三)

版本說明

springboot starter : 0.1.1

dubbo版本: 2.6.2

自動配置類

@Configuration
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true, havingValue = "true")
@ConditionalOnClass(AbstractConfig.class)
public class DubboAutoConfiguration {

    
   // 單個dubbo配置繫結bean , 預設就是單個
    @EnableDubboConfig
protected static class SingleDubboConfigConfiguration { } /** * 多個dubbo配置繫結bean , 預設不使用。 * */ @ConditionalOnProperty(name = MULTIPLE_CONFIG_PROPERTY_NAME, havingValue = "true") @EnableDubboConfig(multiple = true) protected static class MultipleDubboConfigConfiguration
{ } /** * service類,服務提供者的BeanDefinitionRegistryPostProcessor類,用來解析 * @Service註解,生成Service的BeanDefinition類,放入spring容器,供spring容器生成Bean * */ @ConditionalOnProperty(name = BASE_PACKAGES_PROPERTY_NAME) @ConditionalOnClass(RelaxedPropertyResolver.class) @Bean public
ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(Environment environment) { RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment); Set<String> packagesToScan = resolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet()); return new ServiceAnnotationBeanPostProcessor(packagesToScan); } // springboot dataBinder 機制的擴充套件,用來將具體的屬性設定到相應的實體類裡面去。 @ConditionalOnClass(RelaxedDataBinder.class) @Bean @Scope(scopeName = SCOPE_PROTOTYPE) public RelaxedDubboConfigBinder relaxedDubboConfigBinder() { return new RelaxedDubboConfigBinder(); } /** * 用來解析@Reference 註解,消費者引用哪些服務,通過這個註解來進行引用 * 給標註這個@Reference註解的屬性賦值, 和@autowired的做法類似。 * */ @ConditionalOnMissingBean @Bean(name = ReferenceAnnotationBeanPostProcessor.BEAN_NAME) public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() { return new ReferenceAnnotationBeanPostProcessor(); } }

配置說明:

SingleDubboConfigConfiguration : 引入了單個dubbo配置繫結bean的配置 , 預設使用

// 配置如下
dubbo.application
dubbo.module
dubbo.registry
dubbo.protocol
dubbo.monitor
dubbo.provider
dubbo.consumer

**MultipleDubboConfigConfiguration ** :多個dubbo配置繫結bean , 預設不使用。Dubbo @Service@Reference 允許 Dubbo 應用關聯ApplicationConfig Bean 或者指定多個RegistryConfig Bean 等能力。換句話說,Dubbo 應用上下文中可能存在多個ApplicationConfig 等 Bean定義。

// 配置如下
dubbo.applications
dubbo.modules
dubbo.registries
dubbo.protocols
dubbo.monitors
dubbo.providers
dubbo.consumers

**serviceAnnotationBeanPostProcessor ** :解析service類註解的類,如果在spring boot啟動類上配置了@DubboComponentScan 則預設不使用。

**referenceAnnotationBeanPostProcessor ** : 為@Reference注入物件,如果在spring boot啟動類上配置了@DubboComponentScan 則預設不使用。

因為在@DubboComponentScan這個註解中引入了DubboComponentScanRegistrar這個註冊類,該類中做了解析@service註解和@Reference的事情

@EnableDubboConfig

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationSelector.class)  // 主要作用是這個
public @interface EnableDubboConfig {

    /**
     * It indicates whether binding to multiple Spring Beans.
     *
     * @return the default value is <code>false</code>
     * @revised 2.5.9
     */
    boolean multiple() default false;

}

主要的作用就是匯入了這個類DubboConfigConfigurationSelector

DubboConfigConfigurationSelector

public class DubboConfigConfigurationSelector implements ImportSelector, Ordered {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		
        // 獲取註解上的屬性,這個是通過@EnableDubboConfig匯入的,所以AnnotationMetadata裡面就包含了這個註解的值
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));

        // 是否是多配置,預設為false
        boolean multiple = attributes.getBoolean("multiple");

        if (multiple) {
            return of(DubboConfigConfiguration.Multiple.class.getName());
        } else {
            // 這裡就直接講解單配置的。
            return of(DubboConfigConfiguration.Single.class.getName());
        }
    }

    private static <T> T[] of(T... values) {
        return values;
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }


}

DubboConfigConfigurationSelector這個類實現了ImportSelector 介面,該介面的selectImports方法就是返回bean的名稱,供spring初始化,所以這裡返回了

DubboConfigConfiguration.Single.class.getName() , spring就會初始化這個類了。

Single

DubboConfigConfiguration.Single的程式碼如下 , 通過@EnableDubboConfigBindings註解,匯入了多個@EnableDubboConfigBinding

@EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class)
    })
    public static class Single {

    }

EnableDubboConfigBindings

由上面可以看到,spring在初始化Single這個類的時候,必然會載入他上面的註解,該類的主要作用就是為了匯入它上面的註解,@EnableDubboConfigBindings

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboConfigBindingsRegistrar.class)  // 匯入了這個類。
public @interface EnableDubboConfigBindings {

    /**
     * The value of {@link EnableDubboConfigBindings}
     *
     * @return non-null
     */
    EnableDubboConfigBinding[] value();

}

@EnableDubboConfigBindings 註解匯入了DubboConfigBindingsRegistrar這個類,該類的作用是將配置屬性和dubbo的配置進行繫結。註解的value是7個子註解

@EnableDubboConfigBinding ,後面DubboConfigBindingsRegistrar解析的時候,會獲取到這個7個子註解,將對應的屬性和dubbo的配置類進行繫結。

DubboConfigBindingsRegistrar

public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private ConfigurableEnvironment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 獲取匯入此類的註解資訊,@EnableDubboConfigBindings
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));
		
        // 獲取@EnableDubboConfigBinding 子註解
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

        // 初始化DubboConfigBindingRegistrar類,該類的主要作用就是為了解析單個的@EnableDubboConfigBinding註解
        DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
        registrar.setEnvironment(environment);

        for (AnnotationAttributes element : annotationAttributes) {
			// 迴圈註冊,通過註解裡面的資訊,生成Dubbo配置的BeanDefinition,最後放入spring容器中,供spring容器例項化。
            registrar.registerBeanDefinitions(element, registry);

        }
    }

    @Override
    public void setEnvironment(Environment environment) {

        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);

        this.environment = (ConfigurableEnvironment) environment;

    }

}

註冊dubbo的配置bean

protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {
		//1. 從環境 中取出 響應的屬性名   
        String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));
        // 2. 獲取dubbo的配置類的class
        Class<? extends AbstractConfig> configClass = attributes.getClass("type");
        // 3. 獲取是否是多個dubbo的配置
        boolean multiple = attributes.getBoolean("multiple");
       // 註冊dubbo的配置bean
        registerDubboConfigBeans(prefix, configClass, multiple, registry);

    }

步驟說明:

1.引數attributes就是@EnableDubboConfigBinding裡面的屬性,獲取prefix屬性值,就是獲取到了:dubbo.application

例:

@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class)

2.獲取dubbo的配置類的class,也就是獲取到了ApplicationConfig.class

3.獲取multiple的值,預設沒有配置就是false

4.呼叫registerDubboConfigBeans方法生成dubbo的配置bean

 private void registerDubboConfigBeans(String prefix,
                                          Class<? extends AbstractConfig> configClass,
                                          boolean multiple,
                                          BeanDefinitionRegistry registry) {
		// 根據屬性名,如:dubbo.application  獲取具體的屬性值
        Map<String, String> properties = getSubProperties(environment.getPropertySources(), prefix);
      
        if (CollectionUtils.isEmpty(properties)) {
          // 如果沒有配置,則沒有必要生成對應的dubbo配置bean了
            if (log.isDebugEnabled()) {
                log.debug("There is no property for binding to dubbo config class [" + configClass.getName()
                        + "] within prefix [" + prefix + "]");
            }
            return;
        }
		// BeanName
        Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
                Collections.singleton(resolveSingleBeanName(properties, configClass, registry));

        for (String beanName : beanNames) {
            // 生成bena
            registerDubboConfigBean(beanName, configClass, registry);
            // 註冊dubbo的DubboConfigBindingBeanPostProcessor
            registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);

        }

    }

    private void registerDubboConfigBean(String beanName, Class<? extends AbstractConfig> configClass,
                                         BeanDefinitionRegistry registry) {
		// 生成BeanDefinitionBuilder
        BeanDefinitionBuilder builder = rootBeanDefinition(configClass);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
		// 通過BeanDefinitionRegistry註冊dubbo的配置bean
        registry.registerBeanDefinition(beanName, beanDefinition);

        if (log.isInfoEnabled()) {
            log.info("The dubbo config bean definition [name : " + beanName + ", class : " + configClass.getName() +
                    "] has been registered.");
        }

    }

DubboConfigBindingBeanPostProcessor

這個類是在dubbo的配置類初始化完成後會執行響應的方法。用來將屬性值設定到對應的屬性裡面去,在springboot中我們存在這種情況

first-namefirstName, FIRST_NAME , 比如我們在yaml檔案中配置這樣的屬性,我們的java bean中的屬性是firstName , 在使用@ConfigurationProperties

註解的時候我們無需擔心,如果不是用springboot自身的config類來注入,那麼我們自己處理這種情況就會變的非常麻煩,所以dubbo選擇的是通過RelaxedDataBinder類來處理這個問題。這是spring boot的機制。

DubboConfigBindingBeanPostProcessor類實現了BeanPostProcessor,ApplicationContextAware, InitializingBean 這三個介面,下面是挑了一些重要的方法展示出來 , **每個dubbo配置類都有相應的DubboConfigBindingBeanPostProcessor **

public DubboConfigBindingBeanPostProcessor(String prefix, String beanName) {
  
        Assert.notNull(prefix, "The prefix of Configuration Properties must not be null");
        Assert.notNull(beanName, "The name of bean must not be null");
        this.prefix = prefix; // 屬性字首
        this.beanName = beanName;  // dubbo的配置類名
    }
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  // 會在每一個bean例項化之後、初始化(如afterPropertiesSet方法)之前被呼叫。
  if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) {
	
    AbstractConfig dubboConfig = (AbstractConfig) bean;
    // 將屬性和配置進行繫結
    dubboConfigBinder.bind(prefix, dubboConfig);

    if (log.isInfoEnabled()) {
      log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
               "configuration properties : " + prefix);
    }
  }

  return bean;

}


@Override
public void afterPropertiesSet() throws Exception {
  // DubboConfigBindingBeanPostProcessor 初始化之後就會執行
  if (dubboConfigBinder == null) {
    try {
      // 從容器中獲取DubboConfigBinder , DubboConfigBinder的作用範圍是prototype , 每次呼叫getbean都會新建立一個
      dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class);
    } catch (BeansException ignored) {
      if (log.isDebugEnabled()) {
        log.debug("DubboConfigBinder Bean can't be found in ApplicationContext.");
      }
      // Use Default implementation
      dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment());
    }
  }

  dubboConfigBinder.setIgnoreUnknownFields(ignoreUnknownFields);
  dubboConfigBinder.setIgnoreInvalidFields(ignoreInvalidFields);

}

dubboConfigBinder的程式碼如下,下面主要就是呼叫springboot 的dataBinder機制進行屬性設值了

public class RelaxedDubboConfigBinder extends AbstractDubboConfigBinder {

    @Override
    public <C extends AbstractConfig> void bind(String prefix, C dubboConfig) {
        RelaxedDataBinder relaxedDataBinder = new RelaxedDataBinder(dubboConfig);
        // Set ignored*
        relaxedDataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields());
        relaxedDataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields());
        //從Environment中獲取屬性
        Map<String, String> properties = getSubProperties(getPropertySources(), prefix);
        // 將屬性MAP轉換為MutablePropertyValues
        MutablePropertyValues propertyValues = new MutablePropertyValues(properties);
        // 繫結
        relaxedDataBinder.bind(propertyValues);
    }
}

通過上面的原始碼,可以看出來,dubbo的配置是一環接著一環,很多時候一個不起眼的地方就是往下走的關鍵程式碼,他主要是通過註解的匯入配置類,然後通過

BeanDefinitionRegistry生成對應的beanDefintion放入spring容器中。

本文所解析的這些原始碼均不涉及dubbo的核心功能,僅僅是講了dubbo啟動之後,如何獲取到配置,如果進行配置裝配,方便大家後續有個好的理解。

有興趣可以看下一spring的擴充套件機制,dubbo中都有大量的使用到。