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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

版本说明

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

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

sharedCode源码交流群,欢迎喜欢阅读源码的朋友加群,添加下面的微信, 备注”加群“ 。

20191017100244\_1.png

20191017100244\_2.png


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » dubbo系列之spring boot核心配置读取(三)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏