9. Dubbo与Spring整合 - @DubboService
1. 源码分析
本文主要介绍Dubbo
里@DubboService
注解的解析过程。
1.1 配置扫描路径
DubboComponentScan
可以指定扫描路径,也可以指定扫描类。
例如:
// 指定扫描路径方式
@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")
// 指定扫描类对应的包名方式
@EnableDubbo(scanBasePackageClasses = ProviderConfiguration.class)
Spring在解析DubboComponentScan
注解时,会导入DubboComponentScanRegistrar
这个类,这个类是解析Dubbo注解、路径的入口类。
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
1.2 解析扫描路径以及Dubbo注解
DubboComponentScanRegistrar
类实现了ImportBeanDefinitionRegistrar
接口,在spring启动时,会调用registerBeanDefinitions
方法。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceClassPostProcessor(packagesToScan, registry);
registerCommonBeans(registry);
}
这个方法主要处理过程如下:
- 获取扫描路径
- 扫描解析并处理
@DubboService
注解 - 处理
@DubboReference
注解
1.3 获取扫描路径
该方法解析DubboComponentScan
注解,获取注解属性值,即basePackages
、basePackageClasses
、value
的值。其中,basePackages
和value
都是路径,而basePackageClasses
是一个类的集合,Dubbo会截取类的package
路径,最终返回的是解析路径的集合。
1.4 注册BeanPostProcessor
该方法主要作用为:
- 把
ServiceClassPostProcessor
注册为一个Bean - 由于
ServiceClassPostProcessor
实现了BeanDefinitionRegistryPostProcessor
接口,所以在Spring启动时会调用postProcessBeanDefinitionRegistry
方法 - 扫描哪些类上加了
@DubboService
注解了,把这些类解析并生成BeanDefinition
,生成两个类,一个为普通的Bean
,一个为ServiceBean
- 在生成的
ServiceBean
中会监听ServiceBeanExportedEvent
事件,在Spring容器启动完后,对Dubbo服务
进行导出
private void registerServiceClassPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceClassPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
此方法,将ServiceClassPostProcessor
类生成为RootBeanDefinition
,把扫描的路径作为ServiceClassPostProcessor
类的构造方法参数,设置Bean的角色为ROLE_INFRASTRUCTURE
与AOP有关,然后将生成的BeanDefinition
进行注册。
ServiceClassPostProcessor
实现类BeanDefinitionRegistryPostProcessor
接口,在spring启动的时候,会调用postProcessBeanDefinitionRegistry
方法。
1.5 处理BeanDefinition
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
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!");
}
}
}
此方法,现将DubboBootstrapApplicationListener
注册成一个Bean,主要是用来监听spring的刷新和关闭事件。
调用resolvePackagesToScan
方法对传进来的路径进行解析,替换占位符。
调用registerServiceBeans
方法将对扫描到的类注册为ServiceBean
。
1.6 注册ServiceBean
先定义一个扫描器,对传进来的扫描路径扫描哪些类上含有DubboService
、org.apache.dubbo.config.annotation.Service
、com.alibaba.dubbo.config.annotation.Service
3个注解,如果类上有这些注解,就把这些类生成对应的BeanDefinition
,然后再把BeanDefinition
注册为Spring容器里的Bean。
拿到了BeanDefinition
,接下来就该把BeanDefinition
生成对应ServiceBean
。通过调用registerServiceBean
方法注册ServiceBean
该方法主要处理过程如下:
- 调用
resolveClass
方法,将BeanDefinition
中的beanClassName
进行加载,生成类对象。 - 解析
@DubboService
注解,获取注解里配置的属性值。 - 解析实现类对应的接口。
- 获取
BeanDefinition
中的beanName
,为ServiceBean
的ref
属性值做准备。 - 调用
buildServiceBeanDefinition
构造出ServiceBeanDefinition
。 - 将构造出的
ServiceBeanDefinition
进行注册。
1.7 构建DubboService的BeanDefinition
该方法主要处理过程如下:
- 将
ServiceBean
生成一个RootBeanDefinition
- 拿到这个
BeanDefinition
所有属性 - 通过
propertyValues.addPropertyValues
,将@DubboService
注解配置的信息赋值给ServiceBean
对应的BeanDefinition
。被忽略的属性ignoreAttributeNames
除外,因为这些属性有些特殊,不是简单数据类型,需要进行特殊处理。propertyValues
可以对一些简单的数据类型进行赋值,比如String、int等。 - 调用
addPropertyReference
方法为ref
属性进行赋值,传进去的参数数beanName
,spring 根据beanName
去找到对应的bean对象,赋值是beanName
对应的具体对象。 - 为其他刚才忽略的属性进行赋值,
provider
、application
、module
等。 - 像
registry
、protocol
可以为多个值的属性,进行转换为RuntimeBeanReference
集合进行赋值。
后记
@DubboService
解析以及处理基本上已经结束了,至于Dubbo是如何对外暴露提供服务的,这块后面再进行介绍。