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:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章