前面一段時間分析了Spring 和Dubbo,本文將以Dubbo中對Spring 集成爲切入點進行分析。
從Spring中 Dubbo的使用上面來看,有兩個重要的註解,即@Service
和 @Reference
。
對兩個註解的解析分別在 ServiceAnnotationBeanPostProcessor
和 ReferenceAnnotationBeanPostProcessor
這類ReferenceAnnotationBeanPostProcessor
和前面Spring 中的 AutowiredAnnotationBeanPostProcessor
類似,同樣是在實例化後,初始化前,執行 postProcessMergedBeanDefinition
進行註解掃描,而在populateBean
中進行註解屬性注入。
本文從以下幾個方面展開:
1.
Spring
和 Dubbo
集成使用,可以使用 @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
註解,主要目的是爲其他註解做一層包裝,是一個組合註解,包括以下註解:
EnableDubboConfig
DubboComponentScan
EnableDubboLifecycle
EnableDubboConfig
EnableDubboConfig
主要是定義一些Dubbo內部組件配置以及值獲取方式,包括以下:
ApplicationConfig
: dubbo.applicationModuleConfig
: dubbo.moduleRegistryConfig
: dubbo.registryProtocolConfig
: dubbo.protocolMonitorConfig
: dubbo.monitorProviderConfig
: dubbo.providerConsumerConfig
: dubbo.consumer
而如果配置是支持多份配置,則對應的配置文件前綴爲:
ApplicationConfig
: dubbo.applicationsModuleConfig
: dubbo.modulesRegistryConfig
: dubbo.registriesProtocolConfig
: dubbo.protocolsMonitorConfig
: dubbo.monitorsProviderConfig
: dubbo.providersConsumerConfig
: 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 的容器中:
DubboConfigConfiguration.Single
和DubboConfigConfiguration.Multiple
。這兩個類實際上也是兩個包裝,具體是通過EnableConfigurationBeanBinding
將dubbo
框架運行所必須的組件進行聲明並注入。
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
的 @Import
的 ImportBeanDefinitionRegistrar
爲: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
實現了 BeanFactoryPostProcessor
和 BeanPostProcessor
接口
所以有以下幾個方法會執行
- 在bean進行完全掃描之後,即在
Spring
的refresh
中invokeBeanFactoryPostProcessors
中 執行postProcessBeanFactory
- 在
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);
}
關於 ServiceAnnotationBeanPostProcessor
和 ReferenceAnnotationBeanPostProcessor
作用原理,看博主下篇文章分析。
EnableDubboLifecycle
還是直接看 引入的 DubboLifecycleComponentRegistrar
:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registerBeans(registry, DubboLifecycleComponentApplicationListener.class);
registerBeans(registry, DubboBootstrapApplicationListener.class);
}
註冊了兩個bean DubboLifecycleComponentApplicationListener
和 DubboBootstrapApplicationListener
.
兩個類都繼承了 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;
}
- 執行
initialize
確保初始化了所有配置。 - 將所有要暴露的服務進行具體暴露,即
exportServices
- 暴露元數據中心
exportMetadataService
- 執行所有
ReferenceBean
的具體refer方法
對於 DubboLifecycleComponentApplicationListener
類,則是給予Spring 容器聲明週期,對Dubbo框架內,所有 org.apache.dubbo.common.context.Lifecycle
的類,進行相關的監聽和調用。
覺得博主寫的有用,不妨關注博主公衆號: 六點A君。
哈哈哈,一起研究Spring: