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

精品源碼


Eureka精品源碼java

Quartz系列全集
spring

xxl-job系列springboot

sharding-jdbc精品源碼合集
微信

構建sleuth+zipkin全鏈路監控系統(完結篇)
app

版本說明

springboot starter : 0.1.1ide

dubbo版本: 2.6.2post

自動配置類

@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();
    }

}

配置說明:ui

SingleDubboConfigConfiguration : 引入了單個dubbo配置綁定bean的配置 , 默認使用this

// 配置以下
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定義。url

// 配置以下
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 ImportSelectorOrdered {

    @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 ImportBeanDefinitionRegistrarEnvironmentAware {

    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中都有大量的使用到。

https://nobodyiam.com/2017/02/26/several-ways-to-extend-spring/


本文分享自微信公衆號 - sharedCode(sharedCode)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索