dubbo源碼閱讀-配置(三)之properties屬性配置原理


示例代碼

public class Provider {
    /**
     * In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before
     * launch the application
     */
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }

    @Configuration
    /**
     * <1>內部包含import注解 作為初始化 @EnableDubbo只是一個合並注解  具體看內部。。可以使用內部注解分開使用
     * 快速入門:https://www.cnblogs.com/LQBlog/p/15410425.html
     */
    @EnableDubbo(scanBasePackages = "provider") //
    @PropertySource("classpath:dubbo-provider.properties") // <2> spring 注解的方式加載properties
    static class ProviderConfiguration {
    }
}

dubbo-provider配置

dubbo.application.name=soa-promotion-provider
dubbo.registry.address=127.0.0.1:9080
dubbo.registry.check=false
dubbo.protocol.name=dubbo
dubbo.protocol.port=23888
dubbo.protocol.threads=500
dubbo.protocol.dispatcher=message
dubbo.provider.delay=-1
dubbo.provider.retries=false

配置之后就會在容器里面創建對應的config對象

<1>EnableDubbo

@Target({ElementType.TYPE})//只能打在類上
@Retention(RetentionPolicy.RUNTIME)//保留到運行時
@Inherited//被此注解修飾的注解,打在某實現類上,有其他類繼承實現類,則也會繼承此注解
@Documented//打在某個類上的時候 類生成java doc時此注解會生成出來
@EnableDubboConfig//<2>@Import注解位置 記錄一下 還支持實現ImportSelector實現spring 切入 
@DubboComponentScan
public @interface EnableDubbo {
    /**
     * spring的注解 后續可以通過spring api獲取DubboComponentScan 拿到DubboComponentScan設置的值
     * @return
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    /**
     * DubboComponentScan的合並注解
     * @return
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
    /**
     * DubboComponentScan的合並注解
     * @return
     */
    @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
    boolean multipleConfig() default true;

}

<2>EnableDubboConfig

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)//<3>初始化切入點
public @interface EnableDubboConfig {

    /**
     * 是否支持多配置
     * dubbo.registrys.beaname1.address=192.168.0.1:1111
     * dubbo.registrys.beaname2.address=192.168.0.1:1111
     * @return
     */
    boolean multiple() default true;

}

@Import快速入門:鏈接 

可以理解為當 bean被spring容器初始化則@Import則會被調用

DubboConfigConfigurationRegistrar

<3>DubboConfigConfigurationRegistrar

org.springframework.context.annotation.ImportBeanDefinitionRegistrar

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     *
     * @param importingClassMetadata 包含@Import注解類的其他注解信息
     * @param registry 通過他可以像容器注入bean
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        /**
         * 獲取EnableDubboConfig 注解信息
         */
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
        /**
         * 獲取multiple 默認為true 表示是否支持多個config配置
         * 參考
         */
        boolean multiple = attributes.getBoolean("multiple");
        /**
         * 初始化單個配置config
         * <4>注意此類打了@Import注解 繼續往下看
         */
        registerBeans(registry, DubboConfigConfiguration.Single.class);

        /**
         * <4>如果開啟多個配置 則初始化多個配置的config
         * dubbo.applications.beanname.id=ddd
         * dubbo.registrys.beaname2.address=192.168.0.1:1111
         * dubbo.registrys.beaname2.address=192.168.0.1:2222
         */
        if (multiple) { // Since 2.6.6 https://github.com/apache/incubator-dubbo/issues/3193
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }
    }
}

<4>DubboConfigConfiguration

public class DubboConfigConfiguration {
    /**
     * <5>定義了單個配置 每個前綴對應的 類
     */
    @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 {

    }
    /**
     * <5>定義了多個配置每個前綴對應的類
     */
    @EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true)
    })
    public static class Multiple {

    }
}

<5>EnableDubboConfigBindings

/**
 * Multiple {@link EnableDubboConfigBinding} {@link Annotation}
 *
 * @since 2.5.8
 * @see EnableDubboConfigBinding
 */
@Target({ElementType.TYPE}) //注解限制打在的地方
@Retention(RetentionPolicy.RUNTIME) //注解保留時期 這里是保留到運行時
@Documented //標識被javadoc記錄
@Import(DubboConfigBindingsRegistrar.class)//注意看<3>.4處會注冊打上此注解的實例到容器 所以會觸發Import <6>@Import 
public @interface EnableDubboConfigBindings {

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

}

DubboConfigBindingsRegistrar

<6>registerBeanDefinitions

com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingsRegistrar#registerBeanDefinitions

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //獲取EnableDubboConfigBindings注解實例
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));
        //獲得values注解實例 EnableDubboConfigBinding
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
        DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
        registrar.setEnvironment(environment);
        //循環遍歷EnableDubboConfigBinding配置<4>處 能夠獲取到各個前綴和class的映射關系
        for (AnnotationAttributes element : annotationAttributes) {
            //<7> 負責解析前綴和class映射關系
            registrar.registerBeanDefinitions(element, registry);

        }
    }

DubboConfigBindingRegistrar

<7>registerBeanDefinitions

com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerBeanDefinitions

 protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {

        /***
         *   獲得注解配置的前綴 比如
         *   dubbo.application
         *    environment.resolvePlaceholders是為了避免我們配置的占位符所以轉換一下
         *    比如${applictionPrefix}.name=
         */
        String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));

        /**
         * 獲取對應的class 如前綴是dubbo.application class就是com.alibaba.dubbo.config.ApplicationConfig
         * 泛型<extends AbstractConfig>限制class必須繼承AbstractConfig
         */
        Class<? extends AbstractConfig> configClass = attributes.getClass("type");

        /**
         * 是否是多個配置
         */
        boolean multiple = attributes.getBoolean("multiple");

        /**
         * <8>創建對應的config bean
         */
        registerDubboConfigBeans(prefix, configClass, multiple, registry);

    }

<8>registerDubboConfigBeans

com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerBeanDefinitions

->

com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerDubboConfigBeans

private void registerDubboConfigBeans(String prefix,
                                          Class<? extends AbstractConfig> configClass,
                                          boolean multiple,
                                          BeanDefinitionRegistry registry) {

        //獲得指定前綴的屬性 如:dubbo.application.name=333  key=name value=333
        Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);

        if (CollectionUtils.isEmpty(properties)) {
            if (log.isDebugEnabled()) {
                log.debug("There is no property for binding to dubbo config class [" + configClass.getName()
                        + "] within prefix [" + prefix + "]");
            }
            return;
        }
        /**
         * 配置的mltiple為false取得配置文件配置的id作為beanName(id)
         * 如果沒獲取到則生成類全名稱#index 疊加
         * multip是true則解析
         * dubbo.applications.beanname1.name=ffff
         * dubbo.applications.beanname2.namee=ffff2
         * 這個時候beanname1和beanname2會作為二beanid
         */
        Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
                Collections.singleton(resolveSingleBeanName(properties, configClass, registry));

        for (String beanName : beanNames) {

            /**
             * 這里並沒有真正的初始化config對象,只是將bean的定義告訴容器 供容器初始化
             * 初始化后config各個屬性都是空
             */
            registerDubboConfigBean(beanName, configClass, registry);

            /**
             * <9>初始化對應的DubboConfigBindingBeanPostProcessor 用於后面為將properties配置注入到對應的config
             */
            registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);

        }
        //向容器創建NamePropertyDefaultValueDubboConfigBeanCustomizer,供DubboConfigBindingBeanPostProcessor使用
        registerDubboConfigBeanCustomizers(registry);

    }

 <9>registerDubboConfigBindingBeanPostProcessor

com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerBeanDefinitions

->

com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerDubboConfigBeans

->

com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerDubboConfigBindingBeanPostProcessor

 

   private void registerDubboConfigBindingBeanPostProcessor(String prefix, String beanName, boolean multiple,
                                                             BeanDefinitionRegistry registry) {

        //獲得DubboConfigBindingBeanPostProcessor的class
        Class<?> processorClass = DubboConfigBindingBeanPostProcessor.class;
        //獲得DubboConfigBindingBeanPostProcessor的class 的BeanDefinitionBuilder
        BeanDefinitionBuilder builder = rootBeanDefinition(processorClass);
        //獲得前綴
        String actualPrefix = multiple ? normalizePrefix(prefix) + beanName : prefix;
        //告訴容器初始化時 調用2個參數的構造函數 傳入前綴和beanName 供后續使用
        builder.addConstructorArgValue(actualPrefix).addConstructorArgValue(beanName);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        /**
         *   告訴容器創建DubboConfigBindingBeanPostProcessor 對象
         *   實現了InitializingBean 在初始化會先調用<10>
         *   實現了BeanPostProcessor 用於所有bean創建前創建后的前置處理<13>
         */

        registerWithGeneratedName(beanDefinition, registry);

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

    }

DubboConfigBindingBeanPostProcessor

<10>afterPropertiesSet

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#afterPropertiesSet

 @Override
    public void afterPropertiesSet() throws Exception {

        /***
         *  <11><從容器獲取DubboConfigBinder組件 獲取不到則使用 DefaultDubboConfigBinder
         */
        initDubboConfigBinder();
        /**
         *<12>從容器獲取dubboConfigBeanCustomizer組件
         * 默認NamePropertyDefaultValueDubboConfigBeanCustomizer在<8>處初始化
         * 用於給容器里面的config做初始化
         */
        initConfigBeanCustomizers();

    }

<11>initDubboConfigBinder

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#afterPropertiesSet

->

 

 

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#initDubboConfigBinder

    private void initDubboConfigBinder() {

        if (dubboConfigBinder == null) {
            try {
                //從容器獲得這個類型的bean 這里我們可以做自己的擴展
                dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class);
            } catch (BeansException ignored) {
                if (log.isDebugEnabled()) {
                    log.debug("DubboConfigBinder Bean can't be found in ApplicationContext.");
                }
                //獲取不到則使用默認的 並且把環境信息傳入供后續使用
                dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment());
            }
        }

    }

<12>initConfigBeanCustomizers

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#afterPropertiesSet

->

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#initConfigBeanCustomizers

    private void initConfigBeanCustomizers() {

        //從容器獲取DubboConfigBeanCustomizer的實現 可以有多個 注意在<8>處默認初始化了NamePropertyDefaultValueDubboConfigBeanCustomizer在
        Collection<DubboConfigBeanCustomizer> configBeanCustomizers =
                beansOfTypeIncludingAncestors(applicationContext, DubboConfigBeanCustomizer.class).values();

        this.configBeanCustomizers = new ArrayList<DubboConfigBeanCustomizer>(configBeanCustomizers);

        //根據@Sort注解排序
        AnnotationAwareOrderComparator.sort(this.configBeanCustomizers);
    }

 

<13>postProcessBeforeInitialization

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization

由spring調度 實現了BeanPostProcessor 接口 在spring 創建bean之前都會使用Processor做前置和后置處理

  /**
     *bean創建之后的后置處理
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        /**
         *當前config是否能被beanProcessor處理
         * 比如處理 dubbo.application.id=ddd  那么初始化這個config 則會被當時初始化的BeanPostProcessor處理
         *beanName 在<9>處初始化一個前綴對應一個config
         */
        if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) {

            AbstractConfig dubboConfig = (AbstractConfig) bean;

            /**
             *<14>進行綁定
             */
            bind(prefix, dubboConfig);

            /**
             * <15>進行綁定
             */
            customize(beanName, dubboConfig);
        }
        return bean;
    }

    /**
     *
     * 並創建之前的前置處理 並沒有做具體實現
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

<14>bind

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization

->

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#bind

 private void bind(String prefix, AbstractConfig dubboConfig) {

        //<16>傳入前綴和config對象執行bind邏輯如果沒有指定 這里就是調用默認的DefaultDubboConfigBinder
        dubboConfigBinder.bind(prefix, dubboConfig);

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

<15>customize

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization

->

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#customize

    private void customize(String beanName, AbstractConfig dubboConfig) {

        //<8>處默認初始化了一個 NamePropertyDefaultValueDubboConfigBeanCustomizer
        for (DubboConfigBeanCustomizer customizer : configBeanCustomizers) {
            //<17>
            customizer.customize(beanName, dubboConfig);
        }

    }

DefaultDubboConfigBinder

<16>bind

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization

->

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#bind

->

com.alibaba.dubbo.config.spring.context.properties.DefaultDubboConfigBinder#bind

public class DefaultDubboConfigBinder extends AbstractDubboConfigBinder {

    @Override
    public <C extends AbstractConfig> void bind(String prefix, C dubboConfig) {
        DataBinder dataBinder = new DataBinder(dubboConfig);
        // Set ignored*
        dataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields());
        dataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields());
        // 獲得指定前綴的配置信息 如 dubbo.application.name=aaa  則返回key=name  value=aaa
        Map<String, Object> properties = getSubProperties(getPropertySources(), prefix);
        //用於將配置綁定
        MutablePropertyValues propertyValues = new MutablePropertyValues(properties);
        // 這個時候propertie配置的信息 將綁定到config對象里面
        dataBinder.bind(propertyValues);
    }

}

NamePropertyDefaultValueDubboConfigBeanCustomizer

<17>customize

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization

->

com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#customize

->

com.alibaba.dubbo.config.spring.context.config.NamePropertyDefaultValueDubboConfigBeanCustomizer#customize

    public void customize(String beanName, AbstractConfig dubboConfigBean) {

        //獲得config的Name屬性
        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dubboConfigBean.getClass(), PROPERTY_NAME);
        //是否含有name屬性
        if (propertyDescriptor != null) { // "name" property is present

            //獲得對應的getMethod
            Method getNameMethod = propertyDescriptor.getReadMethod();

            if (getNameMethod == null) { // if "getName" method is absent
                return;
            }
            //獲得name值 正常情況 我們前面bind已經綁定了
            Object propertyValue = ReflectionUtils.invokeMethod(getNameMethod, dubboConfigBean);
            //如果我們配置了 前面會正常bind這里就會為空
            if (propertyValue != null) { // If The return value of "getName" method is not null
                return;
            }

            //如果沒有配置name config又有name屬性 則將傳入的設置 正常情況不會到這里來 可以看<13>處
            Method setNameMethod = propertyDescriptor.getWriteMethod();
            if (setNameMethod != null && getNameMethod != null) { // "setName" and "getName" methods are present
                if (Arrays.equals(of(String.class), setNameMethod.getParameterTypes())) { // the param type is String
                    // set bean name to the value of the "name" property
                    ReflectionUtils.invokeMethod(setNameMethod, dubboConfigBean, beanName);
                }
            }
        }
    }

對於parameters配置

dubbo.application.parameters['validation']=jvalidation

總結

properties配置是如何初始化的

1.EnableDubbo 是一個組合注解 這個注解上打上了@EnableDubboConfig注解 具體初始化時通過EnableDubboConfig 注解處理的

2.此注解有個@Import(DubboConfigConfigurationRegistrar.class)作為初始化切入點

3.DubboConfigConfigurationRegistrar會根據注解配置的multiple是true還是false判斷是否讀取支持多配置來判斷是否像容器注冊DubboConfigConfiguration.Single,DubboConfigConfiguration.Single或者Multiple

4.這2個類上面也打了@EnableDubboConfigBindings 配置了配置前綴和config的class映射關系 具體看<4><5>

5.EnableDubboConfigBindings此注解也打了@Import(DubboConfigBindingsRegistrar.class)作為切入點

6.DubboConfigBindingsRegistrar內部會通過config和映射關系 像spring注入一個config實例

7.同時像容器注冊一個DubboConfigBindingBeanPostProcessor 傳入前綴和config beanName

8.DubboConfigBindingBeanPostProcessor BeanPostProcessor, InitializingBean

9.通過afterPropertiesSet在初始化2個綁定器

10.通過BeanPostProcessor 在spring創建對象后置 后判斷這個處理器是不是處理這個類型 如果是 則通過綁定器將配置信息通過反射設置到config對象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM