前面一段时间分析了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: