zoukankan      html  css  js  c++  java
  • dubbo源码阅读-配置(三)之属性配置原理

    示例代码

    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/yichunguo/p/12122598.html
         */
        @EnableDubbo(scanBasePackages = "provider") //
        @PropertySource("classpath:dubbo-provider.properties") // <2>
        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
    @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对象

  • 相关阅读:
    vscode 的tab空格设置设置为4的方法
    【vue生命周期】- 详解
    javascript中call()、apply()、bind()的用法终于理解
    彻底理解js中this的指向,不必硬背。
    OKR群:为什么说每个程序员都应该有自己的个人OKR
    2019年程序员最值得学习的思维利器——任务分解
    代码之美——《重构》、《代码整洁之道》
    为什么说程序员都应该写一写博客
    我的第一篇博客
    为什么说程序员都应该玩一玩GitHub
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12410953.html
Copyright © 2011-2022 走看看