前言
- 上篇博客Dubbo2.7.3版本源码学习系列一: 初始Dubbo利用SPI机制实现AOP和IOC的源码分析我们详细了解了Dubbo的一些SPI扩展机制。接下来我们学习一下Dubbo注解版本的
@EnableDubbo
注解实现原理 - 准备工作: 需要拉取Dubbo 2.7.3版本的源码(里面有学习过程中的源码注释), 可以执行如下操作
git clone https://github.com/AvengerEug/dubbo.git -b dubbo-2.7.3-myself
- 源码是我
Fork
过来了,没有做任何改动。可以放心clone
废话不多说,直接看源码。
一、官网dubbo-demo-annotation模块
- 源码clone下来后,可以进入根目录执行
mvn clean install -Dmaven.test.skip=true
命令来编译项目(这里要跳过测试步骤) - 找到dubbo-demo/dubbo-demo-annotation/dubbo-demo-annotation-provider模块,并打开
Application.java
文件,如下图:
我只是把我的注册中心的配置改成了zookeeper
, 不改应该也没关系。
通过Application.java
文件可以看到,内部维护了一个静态类ProviderConfiguration
, 并将此静态类作为参数传递到了AnnotationConfigApplicationContext
中。看过我之前的spring相关的博客的话,应该能明白,这是注册了一个beanDefinition。其实很简单。当执行完内部的register
方法后,spring bean工厂DefaultListableBeanFactory
中就会多一条beanDefinition。即一共是7条(在之前的博客中有讲到,这里就不多阐述了)。当spring执行refresh方法并执行到内部的invokeBeanFactoryPostProcessors
方法时,则会去解析配置类(之前的spring系列也讲过)
。此时只有传入的ProviderConfiguration
是配置类,于是开始解析它。解析的过程呢,就会去解析它的@PropertySource注解
、@ComponentScan
、@Import
、@ImportResource
、@Bean标识的方法
,除了@ComponentScan
注解会立即扫描类并把它们变成beanDefinition加载到bean工厂外,其他的几个注解都是先存到一个集合,后续再统一处理。了解了这么一个流程后,我们再看一下@EnableDubbo
注解长什么样。
- EnableDubbo.java
- EnableDubboConfig.java
- DubboComponentScan.java
通过以上可知,实际上就是两个注解在起作用:(@Import(DubboConfigConfigurationRegistrar.class)和@Import(DubboComponentScanRegistrar.class)
) ,最终他们内部执行了如下四个注解类似的功能,即把四个class类给添加到spring容器(目的就是为了加载添加到spring容器中的dubbo相关的配置bean)。
@Import(DubboConfigConfigurationRegistrar.class)
@Import(DubboComponentScanRegistrar.class)
@Import(DubboConfigBindingsRegistrar.class)
@Import(DubboConfigBindingRegistrar.class)
这四个类长什么样就不展示了,直接展示下这四个类分别作了些什么事:
DubboConfigConfigurationRegistrar.java
事件 | 注意事项 | beanName |
注册DubboConfigConfiguration.Single内部类的BeanDefinition | 此内部类中被Dubbo的自定义注解@EnableDubboConfigBindings和@EnableDubboConfigBinding标识 => 此注解又@Import了两个类分别是DubboConfigBindingBeanPostProcessor和DubboConfigBindingsBeanPostProcessor | dubboConfigConfiguration.Single |
注册DubboConfigConfiguration.Multiple bean内部类的BeanDefinition | 此内部类中被Dubbo的自定义注解@EnableDubboConfigBindings和@EnableDubboConfigBinding标识 => 此注解又@Import了两个类分别是DubboConfigBindingBeanPostProcessor和DubboConfigBindingsBeanPostProcessor | dubboConfigConfiguration.Multiple |
DubboComponentScanRegistrar.java
事件 | 注意事项 | beanName |
注册ServiceAnnotationBeanPostProcessor的BeanDefinition | 此类实现了BeanDefinitionRegistryPostProcessor接口,并且在DubboComponentScanRegistrar中指定了ServiceAnnotationBeanPostProcessor的bean要使用一个参数的构造方法进行构建,参数值为注解中的@EnableDubbo注解中的scanBasePackages方法决定 | org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#0 |
注册ReferenceAnnotationBeanPostProcessor的BeanDefinition | 此类实现了AnnotationInjectedBeanPostProcessor、ApplicationListener接口。且对ServiceBeanExportedEvent和ContextRefreshedEvent事件感兴趣 | referenceAnnotationBeanPostProcessor |
DubboConfigBindingRegistrar.java和DubboConfigBindingsRegistrar.java统一做了如下事
事件 | 注意事项 | beanName |
注册ApplicationConfig的beanDefinition | 无配置dubbo.application的相关配置时,则不注册bean | org.apache.dubbo.config.ApplicationConfig#0 |
注册ModuleConfig的beanDefinition | 无配置dubbo.module的相关配置时,则不注册bean | org.apache.dubbo.config.ModuleConfig#0 |
注册RegistryConfig的beanDefinition | 无配置dubbo.registry的相关配置时,则不注册bean | org.apache.dubbo.config.RegistryConfig#0 |
注册ProtocolConfig的beanDefinition | 无配置dubbo.protocol的相关配置时,则不注册bean | org.apache.dubbo.config.ProtocolConfig#0 |
注册MonitorConfig的beanDefinition | 无配置dubbo.monitor的相关配置时,则不注册bean | org.apache.dubbo.config.MonitorConfig#0 |
注册ProviderConfig的beanDefinition | 无配置dubbo.provider的相关配置时,则不注册bean | org.apache.dubbo.config.ProviderConfig#0 |
注册ConsumerConfig的beanDefinition | 无配置dubbo.consumer的相关配置时,则不注册bean | org.apache.dubbo.config.ConsumerConfig#0 |
注册ConfigCenterBean的beanDefinition | 无配置dubbo.config-center的相关配置时,则不注册bean | org.apache.dubbo.config.ConfigCenterBean#0 |
注册MetadataReportConfig的beanDefinition | 无配置dubbo.metadata-report的相关配置时,则不注册bean | org.apache.dubbo.config.MetadataReportConfig#0 |
注册DubboConfigBindingBeanPostProcessor的beanDefinition | 管理Dubbo配置的后置处理器 | org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor |
注册NamePropertyDefaultValueDubboConfigBeanCustomizer | 自定义的配置类处理器,所有的配置类会共用它 | namePropertyDefaultValueDubboConfigBeanCustomizer |
由上面的表格可以知道,@EnableDubbo
注解向spring中注册了至少5个BeanDefinition(其他的beanDefinition都是基于配置是否存在),其中,我们先关注ServiceAnnotationBeanPostProcessor
。为什么呢?不知道大家还记得ConfigurationClassPostProcessor
类吗?这个类我专门写了两篇博客来介绍他!它之所以那么强就是因为它实现了BeanDefinitionRegistryPostProcessor接口。而如今,@EnableDubbo
注解也往spring注册了一个实现了这个接口的bean — ServiceAnnotationBeanPostProcessor,所以我猜测他应该是作为Dubbo服务提供者的入口的。所以我们立马定位到invokeBeanFactoryPostProcessors方法中去, 最终会调用PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
方法。此方法我在之前也特意总结过,包括他加载后置处理器的顺序,等等。
二、PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法
- 如下图, 同理,第一次执行的肯定是
ConfigurationClassPostProcessor
- 执行完ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法后,spring能扫描到的、配置类导入的任何类都以BeanDefinition的形式存在bean工厂了(
除开使用后置处理器手动添加bean的情况
),所以此时,@EnableDubbo
注解导入的五个beanDefinition(因为还有一个带@Bean的方法)肯定在bean工厂中(注意: 此时bean并还没有被生成), 如下图所示:
ok。按照逻辑走,接下来就会执行除开ConfigurationClassPostProcessor并且实现了Ordered接口的后置处理器了,因为ServiceAnnotationBeanPostProcessor
并没有实现Ordered
接口,于是会去执行没有实现PriorityOrdered
和Ordered
接口的后置处理器(咦,不就是ServiceAnnotationBeanPostProcessor
,终于轮到他了)。
于是,我们直接定位到ServiceAnnotationBeanPostProcessor
中的postProcessBeanDefinitionRegistry方法。
源码如下:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// packagesToScan为@EnableDubbo注解中指定的scanBasePackages参数值处理过后的变量
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 开始注册指定路径的值
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// Dubbo自己实现了一个扫描器,mybatis也自己实现了。不知道自己能不能实现,哈哈哈~~~
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
// 获取一个创建bean name的生成器
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 表示扫描的类要包含org.apache.dubbo.config.annotation.Service注解
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
// 为了兼容,也识别alibaba jar包中的Service注解
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
for (String packageToScan : packagesToScan) {
// 扫描指定路径下带上述两个注解的类
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
// 在这一步主要是把扫描出来的beanDefinition变成BeanDefinitionHolder的包装类
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 将所有的包装类注册至bean工厂
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (logger.isInfoEnabled()) {
logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
beanDefinitionHolders +
" } were scanned under package[" + packageToScan + "]");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
+ packageToScan + "]");
}
}
}
}
- ok.
ServiceAnnotationBeanPostProcessor
作为BeanDefinitionRegistryPostProcessor角色的工作就完成了。按照invokeBeanFactoryPostProcessors
方法的逻辑,现在还需要去执行角色为BeanFactoryPostProcessor
的工作。同样的,它也没实现PriorityOrdered
和Ordered
接口,直接看执行无实现接口的逻辑,如图: - 综上所述:
ServiceAnnotationBeanPostProcessor
的作用就是将Dubbo自实现的@Service标识的类注册到bean工厂去,由spring去生成bean。好了,写了那么大一篇幅来描述ServiceAnnotationBeanPostProcessor
, 那剩下的三个该怎么搞咯?别急嘛,这三个都总结完了,那Dubbo注解版本与spring相关的部分就结束了。。。剩下的三个,后面再更新。。
三、总结
- 本次博客主要总结了@EnabbleDubbo注解在服务提供方的具体作用,即通过ServiceAnnotationBeanPostProcessor后置处理器扫描指定包下所有带Dubbo自己开发的@Service注解的类加载到spring中去。顺便温故了一波spring的相关知识点
- I am a slow walker, but I never walk backwards.