Spring IOC(十三)Dubbo 与Spring 集成 - EnableDubbo原理

前面一段时间分析了Spring 和Dubbo,本文将以Dubbo中对Spring 集成为切入点进行分析。

从Spring中 Dubbo的使用上面来看,有两个重要的注解,即@Service@Reference
对两个注解的解析分别在 ServiceAnnotationBeanPostProcessorReferenceAnnotationBeanPostProcessor

这类ReferenceAnnotationBeanPostProcessor和前面Spring 中的 AutowiredAnnotationBeanPostProcessor 类似,同样是在实例化后,初始化前,执行 postProcessMergedBeanDefinition 进行注解扫描,而在populateBean 中进行注解属性注入。
本文从以下几个方面展开:
1.

SpringDubbo集成使用,可以使用 @EnableDubbo,也可以使用 dubbo提供的 dubbo-spring-boot-project 的starter

使用EnableDubbo方式使用

EnableDubbo 注解定义如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
@EnableDubboLifecycle
public @interface EnableDubbo {

   @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};
   // 扫描路径
   @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};

   // 是否多配置
   @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
   boolean multipleConfig() default true;

}

如上面代码,EnableDubbo 中定义的是包路径和multiple配置。该注解无@Import 注解,主要目的是为其他注解做一层包装,是一个组合注解,包括以下注解:

  1. EnableDubboConfig
  2. DubboComponentScan
  3. EnableDubboLifecycle
EnableDubboConfig

EnableDubboConfig 主要是定义一些Dubbo内部组件配置以及值获取方式,包括以下:

  1. ApplicationConfig: dubbo.application
  2. ModuleConfig: dubbo.module
  3. RegistryConfig: dubbo.registry
  4. ProtocolConfig: dubbo.protocol
  5. MonitorConfig: dubbo.monitor
  6. ProviderConfig: dubbo.provider
  7. ConsumerConfig: dubbo.consumer

而如果配置是支持多份配置,则对应的配置文件前缀为:

  1. ApplicationConfig: dubbo.applications
  2. ModuleConfig: dubbo.modules
  3. RegistryConfig: dubbo.registries
  4. ProtocolConfig: dubbo.protocols
  5. MonitorConfig: dubbo.monitors
  6. ProviderConfig: dubbo.providers
  7. ConsumerConfig: dubbo.consumers

上面表达只是 EnableDubboConfig 会帮你做的事,下面看这个注解是如何帮你完成这些事的呢?

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {

    /**
     * 默认是支持多配置
     */
    boolean multiple() default true;

}

看看 @Import 引入的 DubboConfigConfigurationRegistrar

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 获取注解参数
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
		// 是否支持多配置
        boolean multiple = attributes.getBoolean("multiple");

        // 默认注册 DubboConfigConfiguration.Single.class
        registerBeans(registry, DubboConfigConfiguration.Single.class);
		// 注册 DubboConfigConfiguration.Multiple.class
        if (multiple) { 
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }

        // 注册 DubboConfigAliasPostProcessor
        registerDubboConfigAliasPostProcessor(registry);

        // 注册 NamePropertyDefaultValueDubboConfigBeanCustomizer
        registerDubboConfigBeanCustomizers(registry);

    }

    private void registerDubboConfigBeanCustomizers(BeanDefinitionRegistry registry) {
        registerInfrastructureBean(registry, BEAN_NAME, NamePropertyDefaultValueDubboConfigBeanCustomizer.class);
    }

    /**
     * Register {@link DubboConfigAliasPostProcessor}
     *
     * @param registry {@link BeanDefinitionRegistry}
     * @since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
     */
    private void registerDubboConfigAliasPostProcessor(BeanDefinitionRegistry registry) {
        registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME, DubboConfigAliasPostProcessor.class);
    }

}

在 上面的 DubboConfigConfigurationRegistrar 中,主要注册了4种类型的bean到Spring 的容器中:

  1. DubboConfigConfiguration.SingleDubboConfigConfiguration.Multiple。这两个类实际上也是两个包装,具体是通过 EnableConfigurationBeanBindingdubbo框架运行所必须的组件进行声明并注入。
public class DubboConfigConfiguration {

    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableConfigurationBeanBindings({
            @EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class)
    })
    public static class Single {

    }

    /**
     * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableConfigurationBeanBindings({
            @EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
    })
    public static class Multiple {

    }
}

而对应的 EnableConfigurationBeanBinding@ImportImportBeanDefinitionRegistrar 为:ConfigurationBeanBindingRegistrar.
ConfigurationBeanBindingRegistrar 则是通过以下逻辑,将其初始化并注入到Spring 容器中的:

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

        AnnotationAttributes attributes = fromMap(metadata.getAnnotationAttributes(ENABLE_CONFIGURATION_BINDING_CLASS_NAME));

        registerConfigurationBeanDefinitions(attributes, registry);
    }

    protected void registerConfigurationBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {

        String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));
		// 获取待注入类
        Class<?> configClass = attributes.getClass("type");
		// 获取是否多配置中心
        boolean multiple = attributes.getBoolean("multiple");

        boolean ignoreUnknownFields = attributes.getBoolean("ignoreUnknownFields");

        boolean ignoreInvalidFields = attributes.getBoolean("ignoreInvalidFields");
		// 注册bean
        registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry);
    }

下面看看 registerConfigurationBeans 中做了什么:

    private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple,
                                            boolean ignoreUnknownFields, boolean ignoreInvalidFields,
                                            BeanDefinitionRegistry registry) {

        Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(environment.getPropertySources(), environment, prefix);
		// 如果没有配置,那么就直接退出
        if (CollectionUtils.isEmpty(configurationProperties)) {
            if (log.isDebugEnabled()) {
                log.debug("There is no property for binding to configuration class [" + configClass.getName()
                        + "] within prefix [" + prefix + "]");
            }
            return;
        }
		// 获取待注册的配置类的bean名字,此处要注意是单个还是多个bena类名字
        Set<String> beanNames = multiple ? resolveMultipleBeanNames(configurationProperties) :
                singleton(resolveSingleBeanName(configurationProperties, configClass, registry));

        for (String beanName : beanNames) {
        // 注册bean
            registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields,
                    configurationProperties, registry);
        }
		// 注册数据绑定对象
        registerConfigurationBindingBeanPostProcessor(registry);
    }

registerConfigurationBeans 中前行应该都还好立即,最后一个 registerConfigurationBindingBeanPostProcessor注册数据绑定对象则需要进入深看一番:

    private void registerConfigurationBindingBeanPostProcessor(BeanDefinitionRegistry registry) {
        registerInfrastructureBean(registry, ConfigurationBeanBindingPostProcessor.BEAN_NAME,
                ConfigurationBeanBindingPostProcessor.class);
    }

即向Spring 容器中注册 ConfigurationBeanBindingPostProcessor 类。而 registerInfrastructureBean 方法限定了,只有当容器中没有改beanName类时,才会忘容器中注册,所以不必担心 registerConfigurationBindingBeanPostProcessor 多次调用,多次注册。

下面看看 ConfigurationBeanBindingPostProcessor 以及它重写的方法:

public class ConfigurationBeanBindingPostProcessor implements BeanFactoryPostProcessor, BeanPostProcessor {
}

ConfigurationBeanBindingPostProcessor 实现了 BeanFactoryPostProcessorBeanPostProcessor 接口
所以有以下几个方法会执行

  1. 在bean进行完全扫描之后,即在SpringrefreshinvokeBeanFactoryPostProcessors 中 执行 postProcessBeanFactory
  2. getBean 阶段的 initializeBean中,在每个bean初始化之前执行 postProcessBeforeInitialization ,每个bean初始化后执行 postProcessAfterInitialization

可以猜测下 ConfigurationBeanBindingPostProcessor 目的。
了解Dubbo的同学应该知道,Dubbo有多种类型组件,本文上面也有提到过,都是以 AbstractConfig 为父类,前面可以将Dubbo中需要的组件注册成Spring 的bean,但是呢,Dubbo的各个组件内部都是需要拥有对其他组件引用。
例如,一个接口(Service)暴露了,里面有对 Application的引用,对Register的应用 ,对Module的引用。正式因为这样的设计,Dubbo才能支持多注册中心,并且可以动态制定多份配置。
后面文章分析Spring 数据绑定时,再深入分析这里。

DubboComponentScan

接下来看 DubboComponentScan 中目的,直接看其 引入的 DubboComponentScanRegistrar 类。

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 获取 扫描包路径
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
		// 注册 ServiceAnnotationBeanPostProcessor 
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
 		// 注册 ReferenceAnnotationBeanPostProcessor
        registerReferenceAnnotationBeanPostProcessor(registry);

    }

关于 ServiceAnnotationBeanPostProcessorReferenceAnnotationBeanPostProcessor 作用原理,看博主下篇文章分析。

EnableDubboLifecycle

还是直接看 引入的 DubboLifecycleComponentRegistrar

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registerBeans(registry, DubboLifecycleComponentApplicationListener.class);
        registerBeans(registry, DubboBootstrapApplicationListener.class);
    }

注册了两个bean DubboLifecycleComponentApplicationListenerDubboBootstrapApplicationListener.
两个类都继承了 OneTimeExecutionApplicationContextEventListener ,而 最终是实现了 ApplicationListener 接口,即监听了Spring 的声明周期事件。
对于 DubboBootstrapApplicationListener 来说,则是通过Spring 事件对 DubboBootstrap 进行操作:

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
    // 不同事件不同操作
        if (event instanceof ContextRefreshedEvent) {
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }
    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }

    private void onContextClosedEvent(ContextClosedEvent event) {
        dubboBootstrap.stop();
    }

在 start方法中,做了一下几件事:

    public DubboBootstrap start() {
        if (started.compareAndSet(false, true)) {
            initialize();
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is starting...");
            }
            // 1. export Dubbo Services
            exportServices();

            // Not only provider register
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
                // 2. export MetadataService
                exportMetadataService();
                //3. Register the local ServiceInstance if required
                registerServiceInstance();
            }

            referServices();

            if (logger.isInfoEnabled()) {
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }
  1. 执行 initialize 确保初始化了所有配置。
  2. 将所有要暴露的服务进行具体暴露,即 exportServices
  3. 暴露元数据中心 exportMetadataService
  4. 执行所有ReferenceBean 的具体refer方法

对于 DubboLifecycleComponentApplicationListener 类,则是给予Spring 容器声明周期,对Dubbo框架内,所有 org.apache.dubbo.common.context.Lifecycle 的类,进行相关的监听和调用。

觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,一起研究Spring:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值