dubbo在初始化的时候,充分利用了spring的扩展点,进行初始化,这篇笔记主要记录dubbo是如何利用spring的扩展点来进行初始化的;
这篇笔记只记录dubbo启动的时候的源码,不记录启动过程中服务导出和服务引入的源码
dubbo关键类
@DubboComponentScan
DubboComponentScanRegistrar
ServiceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor
ServiceBean
ReferenceBean
@Service
@Reference
dubbo在进行服务导出和服务引入之前,需要将对应的bean放入到spring容器中,比如:当前在UserService中引入了一个TestService,同时对外提供了一个OrderService的类
那dubbo在启动的时候,会扫描将加了@Service的OrderService先注入到spring的容器中,在spring容器启动完成之后,再去zk进行服务注册
同理在初始化UserService的时候,会对@Reference修饰的变量 TestService进行注入,注入的时候,就会去zk拉取对应的服务提供者,然后生成代理对象,将代理对象注入到UserService中
结论
1.在@DubboComponentScan注解中会import一个DubboComponentScanRegistrar,这个DubboComponentScanRegistrar是ImportBeanDefinitionRegistry的实现类
2.在其registerBeanDefinitions方法中会注入两个bean:ReferenceAnnotationBeanPostProcessor和ServiceAnnotationBeanPostProcessor
3.ServiceAnnotationBeanPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,所以在其postProcessorBeanDefinitionRegistry()方法中,会调用dubboClassPathBeanDefinitionScanner的scan方法,进行扫描,将加了@Service注解的bean扫描到beanDefinitionMap中
添加到beanDefinitionMap中的时候,会把beanClass设置为ServiceBean,这个我觉得和Mybatis的接口在被注入到beanDefinitionMap中的时候有点类似
在初始化bean的时候,如果整个spring容器刷新完成了,serviceBean会监听对应的事件,在监听到之后,开始进行服务导出的处理,这时候,调用的就是dubbo中的代码,完成:在zk中注册服务、开启netty等
3.ReferenceAnnotationBeanPostProcessor是一个后置处理器,会对加了@Reference注解的bean进行处理,如果A这个类中通过@Reference注入了bean,在注入的时候,会被这个后置处理器处理
在一个bean初始化的过程中,会进行属性注入,调用其inject方法,然后在inject方法中,会对要注入的bean进行处理,将referenceClass,也就是要注入的bean,包装成referenceBean,然后调用referenceBean的getObject()方法进行服务引入,在referenceBean.getObject()方法中,返回的是一个代理对象,在其getObject()方法中,会开启完成服务引入,然后在属性注入的时候,注入的就是一个代理对象
源码
@DubboComponentScan
这个注解如果陌生的话,那@EnableDubbo这个注解应该不会陌生
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
}
我们就直接来看@DubboComponentScan这个注解了
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
}
DubboComponentScanRegistrar
可以看到,在其注解上,通过@Import注解,引入了这个类,这个类是ImportBeanDefinitionRegistrar的实现类,关于ImportBeanDefinitionRegistrar这个扩展点,在前面@Import原理
中有介绍过,这个也是spring的扩展点之一,在初始化的过程中,会调用ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions()方法,
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
registerReferenceAnnotationBeanPostProcessor(registry);
}
在这个方法中,这两行代码比较简单,就不贴代码了,主要是调用BeanDefinitionRegistry的registerBeanDefinition方法进行bean的注入,但是这里的注入,只是将bean转换成beanDefinition,然后存入到beanDefinitionMap中
这里分别添加到beanDefinitionMap中的两个bean是:ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor
可以看到,这个类实现了BeanDefinitionRegistryPostProcessor,所以在spring容器启动的时候,会调用该方法,具体是什么时候调用的,这就是spring处理的代码了,只需要按照spring的要求,实现相应的接口,然后将实现类放入到spring容器中即可,具体可以参考前面的spring源码笔记 beanDefinitionRegistryPostProcessor笔记
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 解析要扫描的包
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
// 下面if判断中的代码,就是去扫描@Service注解所标记的类
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
下面这个方法中,会完成对beanDefinition的处理
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 这里是dubbo自己继承spring的ClassPathBeanDefinitionScanner,自己实现了一个扫描类
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 这里是设置该扫描器要过滤的bean,过滤条件是添加了Service注解
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
// 对扫描的包进行遍历,有可能设置多个
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
// 按照指定的路径扫描,并将class转换为beanDefinitionMap
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
// 这里会重新查找一遍,获取到所有的加了@Service注解的beanDefinition
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 这个方法中,会对beanDefinition进行一些处理,简单而言,就是将beanName对应的beanDefinition中的beanClass属性设置为ServiceBean
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 + "]");
}
}
}
}
1、scanner.scan(packageToScan);
在这个方法中,会对加了@Service注解的bean进行扫描,然后将bean注入到beanDefinitionMap中
2、Set beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
在这个方法中,找到所有加了@Service注解的bean,然后返回
3、在registerServiceBean(beanDefinitionHolder, registry, scanner);
在这个方法中,会对找到的所有加了@Service注解的beanDefinition,然后对这些beanDefinition信息进行一些处理;主要是对Service注解中指定的dubbo属性进行解析,解析之后,放到beanDefinition中,然后还会将beanDefinition的beanClass设置为ServiceBean
这个方法的代码就不贴了,要不然代码太多了
然后在将beanDefinition的属性调整了之后,会就把beanDefinition放入到beanDefinitionMap中,调用的也是org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
所以:对于@Service注解的bean,实际会注入到spring容器中两个bean,一个是正常的beanDefinition,beanName也是正常的;另外一个beanDefinition对应的beanName,就是serviceBean:com.xxx等这个格式的,并且其beanClass是serviceBean
ReferenceAnnotationBeanPostProcessor
这是ReferenceAnnotationBeanPostProcessor的类图,可以看到,这个类是一个后置处理器,在该后置处理器中,主要处理的就是@Reference注解,我们来看下这个类中的关键方法
com.alibaba.dubbo.config.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor#postProcessMergedBeanDefinition
/**
* 这是referenceAnnotationBeanPostProcessor的核心方法之一:在第三个后置处理器被调用的时候,会执行该逻辑
* 找到当前bean中,添加了@Reference注解的属性信息,然后在后面第六个后置处理器执行的时候,会依次取出所有的待注入属性
* 进行注入
* @param beanDefinition
* @param beanType
* @param beanName
*/
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanType != null) {
InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
}
具体下面的逻辑我就不贴代码了,其实和@Autowired和@Resource注解解析的套路是一样的,在第三个后置处理器执行的时候,找到要注入的属性,然后在第六个后置处理器执行的时候,就直接从这里的缓存中拿到要注入的属性,然后进行属性注入,接下来我们来看第六个后置处理器的执行方法
com.alibaba.dubbo.config.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor#postProcessPropertyValues
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
}
这里的这个方法也是和AutowireAnnotationBeanPostProcessor得逻辑一样,从上面一行代码中,找到要注入的属性之后,在inject()方法中,进行属性注入
主要需要看的是inject()方法:
在inject方法中,dubbo2.6和dubbo2.9版本是不一样的,具体2.7和2.8版本采用的是哪种方式,我没注意看
不管哪个版本,这个inject方法要完成的操作是一样的,就是从注册中心拉去对应的服务提供者,然后生成一个代理对象注入到当前bean中,这里所谓的拉取服务提供者的过程,也就是服务引入的过程,具体的,可以看ReferenceConfig.get()方法
在2.6和2.9版本中,都是先根据当前要注入的类型,生成一个referenceBean对象,生成这个对象之后,在2.6版本中,会通过proxy动态代理的方式,在调用动态代理的时候,需要一个invocationHandler对象,在buildInvocationHandler对象的时候,去调用referenceConfig的get()方法,然后返回一个代理对象;
但是在2.9的版本中,返回了一个referenceBean对象,然后调用referenceBean的getObject()方法,在其getObject()方法中,会调用ReferenceConfig的get()方法,完成服务引入的过程
在服务引入完成之后,调用field.set()方法完成属性注入
所以:对于@Reference注解,其实就是会在属性注入的时候,生成一个referenceBean对象,将其ref属性设置为@Reference指定的bean;不管是哪个版本,核心的思想就是一样的,都是在属性注入的时候,去完成服务引入的,也就是通过ReferenceBean的getObject()方法完成的
总结而言,dubbo在完成初始化的过程中,用到了spring以下几个扩展点:
1.beanDefinitionRegistryPostProcessor --> ServiceAnnotationBeanPostProcessor
2.beanPostProcessor --> ReferenceAnnotationBeanPostProcessor
3.ImportBeanDefinitionRegistrar --> DubboComponentScanRegistrar
4.FactoryBean --> ReferenceBean