一、前言
本系列为个人Dubbo学习笔记,内容基于《深度剖析Apache Dubbo 核心技术内幕》, 过程参考官方源码分析文章,仅用于个人笔记记录。本文分析基于Dubbo2.7.5版本,由于个人理解的局限性,若文中不免出现错误,感谢指正。
系列文章地址:Dubbo源码分析:全集整理
本文基于 Dubbo 2.7.5 版本。关于该部分逻辑,如有需要可参考:
二、Dubbo 的自动装配
在Springboot 集成 Dubbo 时需要引入如下依赖:
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.5</version>
</dependency>
而 dubbo-spring-boot-starter 会引入如下两个依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-autoconfigure</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-autoconfigure-compatible</artifactId>
<version>2.7.5</version>
</dependency>
上面的两个依赖会通过Springboot的自动装配引入Dubbo的自动装配类:DubboAutoConfiguration、DubboRelaxedBindingAutoConfiguration、DubboRelaxedBinding2AutoConfiguration。
如下图:
关于Springboot的 自动装配,如有需要详参: Spring源码分析十一:Springboot 自动装配。
上面我们看到, Dubbo通过 Springboot的 自动装配引入了下面几个类:
- DubboAutoConfiguration : 用于引入 Dubbo的 配置类。包括 ServiceAnnotationBeanPostProcessor、ReferenceAnnotationBeanPostProcessor、SingleDubboConfigConfiguration、MultipleDubboConfigConfiguration、primaryPropertyResolver 等
- DubboRelaxedBindingAutoConfiguration :注入了宽松配置解析器,用于Springboot 1.x 版本, 解析 Dubbo 的配置
- DubboRelaxedBinding2AutoConfiguration : 功能同 DubboRelaxedBindingAutoConfiguration,用于 Springboot 2.x 版本,解析 Dubbo的配置
三、提供者的启动流程
下面我们以服务提供者的视角来看在 Spring 中Dubbo服务暴露的流程:
- Springboot 启动时会激活
ServiceAnnotationBeanPostProcessor #postProcessBeanDefinitionRegistry
方法。此方法会向容器中注册DubboBootstrapApplicationListener
(如果没有注册) 以及被 @Service (Dubbo 提供的 service 注解) 修饰的 Bean的 ServiceBean 的 BeanDefinition。 - 在容器刷新结束后,会通过 AbstractApplicationContext#finishRefresh 发送容器刷新结束事件(ContextRefreshedEvent),而 DubboBootstrapApplicationListener 会监听此事件,并调用 DubboBootstrap#start 方法来发布 Dubbo 服务。同时 DubboBootstrapApplicationListener 也监听 ContextClosedEvent 事件,用于停止服务暴露。
- DubboBootstrap#start 进行服务暴露。
上面的概述涉及到了 ServiceAnnotationBeanPostProcessor、DubboBootstrapApplicationListener 、DubboBootstrap 几个类,下面我们来看看 ServiceAnnotationBeanPostProcessor 和 DubboBootstrapApplicationListener 的实现,DubboBootstrap 由于篇幅限制,详细逻辑在 Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露 一文中进行分析。
1. ServiceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor 类结构如下:
ServiceAnnotationBeanPostProcessor 的作用有两个:
- 如果容器中没有 DubboBootstrapApplicationListener 的BeanDefinition,则向 Spring 容器中 注册 DubboBootstrapApplicationListener 的 BeanDefinition。
- 向 Spring 容器中 注册被 @Service 注解修饰的类对应的 ServiceBean 的 BeanDefinition。这里的@Service 指的是 Dubbo提供的 @Service注解。在 Spring 中 Dubbo会为每个被 @Service 注解修饰的类创建一个对应的 ServiceBean 来保存当前类暴露的一些信息。而 ServiceAnnotationBeanPostProcessor 会为这些 ServiceBean 生成对应的 BeanDefinition 保存到容器中。
注: Spring在容器中创建Bean时依赖于 BeanDefinition。简单来说,Spring在创建容器中的 Bean时首先会先创建BeanDefinition,随后根据BeanDefinition 再创建Bean,随后将Bean注入到容器中。所以这里创建的都是 BeanDefinition。
下面我们来详细分析:由于 ServiceAnnotationBeanPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,所以我们这里关注 postProcessBeanDefinitionRegistry 方法。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// @since 2.7.5
// 1. 注册 DubboBootstrapApplicationListener BeanDefinition
registerBeans(registry, DubboBootstrapApplicationListener.class);
// 2. 获取指定的 Dubbo扫描路径
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 3. 注册 ServiceBean
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
// 打印日志
}
}
我们按照上面的注释:
- registerBeans 将 DubboBootstrapApplicationListener 的 BeanDefinition 注册到容器中
- resolvePackagesToScan 获取到 Dubbo Bean的 扫描路径
- registerServiceBeans 扫描指定路径下的 Dubbo Bean ,生成对应的 BeanDefinition 注册到容器中。
下面我们详细来看每一步的具体实现。
1.1 registerBeans
registerBeans 方法的具体实现是 AnnotatedBeanDefinitionRegistryUtils#registerBeans,主要作用如果 DubboBootstrapApplicationListener BeanDefinition没有注册,则将 DubboBootstrapApplicationListener 的 BeanDefinition 注册到容器中。其实现如下:
// 入参 的 annotatedClasses 是 DubboBootstrapApplicationListener.class
public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {
if (ObjectUtils.isEmpty(annotatedClasses)) {
return;
}
// Remove all annotated-classes that have been registered
Iterator<Class<?>> iterator = new ArrayList<Class<?>>(asList(annotatedClasses)).iterator();
// 如果容器中已经存在 annotatedClass 的 BeanDefinition,则将其移除,不再进行下面的创建
while (iterator.hasNext()) {
Class<?> annotatedClass = iterator.next();
if (isPresentBean(registry, annotatedClass)) {
iterator.remove();
}
}
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
// 创建 annotatedClasses 的 BeanDefinition,并通过 registry 注册到容器中
reader.register(annotatedClasses);
}
1.2 resolvePackagesToScan
ServiceAnnotationBeanPostProcessor#packagesToScan 实现如下,主要作用是解析 Dubbo配置中的 dubbo.scan
配置的扫描路径并返回。dubbo.scan
配置指定了我们需要扫描的路径,在这个路径下的 类如果被Dubbo注解修饰则会被进一步处理。
// 返回解析后的 dubbo.scan 指定的路径
private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
// 获取Dubbo bean 的扫描路径
Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
for (String packageToScan : packagesToScan) {
if (StringUtils.hasText(packageToScan)) {
// 替换 dubbo.scan 配置中的占位符
String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
resolvedPackagesToScan.add(resolvedPackageToScan);
}
}
// 返回解析后的扫描路径
return resolvedPackagesToScan;
}
1.3 registerServiceBeans
在该方法中, Dubbo会扫描 被 org.apache.dubbo.config.annotation.Service 和 com.alibaba.dubbo.config.annotation.Service 注解修饰的类(版本更高则可能是 DubboService),被这两个注解修饰的类会作为服务提供者暴露处理,而这里会生成对应服务的 ServiceBean 的BeanDefinition。
如:对于 ProviderServiceImpl,实现如下:
@Service(version = "1.0.0", group = "spring")
public class ProviderServiceImpl implements ProviderService {
@Override
public void sayHello() {
System.out.println("hello");
}
@Override
public void sayHello(String msg) {
System.out.println("hello msg = " + msg);
}
@Override
public String sayHelloWorld() {
System.out.println("hello world");
return "hello world";
}
}
在 Spring容器中会存在一个 ProviderServiceImpl 实例,同时会存在一个 ServiceBean 实例保存了ProviderServiceImpl 要暴露的服务信息,其中 ServiceBean 在 容器中的 beanName 生成规则是 ServiceBean:{接口路径}:{版本号}:{分组}
,如下图:
所以 ServiceAnnotationBeanPostProcessor#registerServiceBeans 这里是为了每个 Bean对应的 ServiceBean 生成对应的 BeanDefinition 并注册到容器中。
下面我们来看 ServiceAnnotationBeanPostProcessor#registerServiceBeans的实现,如下:
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 1. 创建 Dubbo 扫描类
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 扫描被 @Service 注解修饰的类,这里的Service是 org.apache.dubbo.config.annotation.Service
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
// 扫描被 @com.alibaba.dubbo.config.annotation.Service 注解修饰的类
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
// 2. 在这里会首先将 被 @Service 修饰的 BeanDefinition 注册到 registry 中
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
// 3. 获取所有 @Service 修饰的 Bean的 BeanDefinitionHolder
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 4. 准备注册 ServiceBean的 BeanDefinition
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
... 日志打印
} else {
... 日志打印
}
}
}
这里我们可以看到 :
- 首先构建扫描器类
DubboClassPathBeanDefinitionScanner
,扫描 被org.apache.dubbo.config.annotation.Service
注解和com.alibaba.dubbo.config.annotation.Service
注解修饰的类。 - 将被
org.apache.dubbo.config.annotation.Service
注解和com.alibaba.dubbo.config.annotation.Service
注解修饰的类 的 BeanDefinition 注册到容器中 :对dubbo.scan
的配置路径进行扫描,需要注意的是scanner.scan(packageToScan);
方法会在扫描到满足条件的类时会直接构建其 BeanDefinition 并注册到 registry 中。也就是说此时被 @Service 修饰的 类的 BeanDefinition 在这一步被注册到容器中。 - 获取所有被
org.apache.dubbo.config.annotation.Service
注解和com.alibaba.dubbo.config.annotation.Service
注解修饰的类 的 BeanDefinitionHolder - . 随后遍历 BeanDefinitionHolder,通过
registerServiceBean(beanDefinitionHolder, registry, scanner);
方法生成 BeanDefinition 并注册到容器中,这里的 BeanDefinition是 ServiceBean的 BeanDefinition。
上面我们看到,ServiceBean的 BeanDefinition 构建交由 registerServiceBean(beanDefinitionHolder, registry, scanner);
来完成,下面我们来看看 registerServiceBean(beanDefinitionHolder, registry, scanner);
的具体实现 :
1.3.1 registerServiceBean
在 ServiceAnnotationBeanPostProcessor#registerServiceBean 中生成了 ServiceBean 的 BeanDefinition 并注册到容器中。我们这里直接来看 registerServiceBean 的实现,如下:
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
// 获取 提供者类的 Class
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// 获取 @Service 注解
Annotation service = findServiceAnnotation(beanClass);
// 获取 @Service 注解的属性
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// 获取暴露的服务接口
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
// 获取 beanName
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 封装成 ServiceBean BeanDefinition
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
// 获取 ServiceBean 的 beanName ,规则是 ServiceBean:{接口路径}:{版本号}:{分组}
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
// 如果 ServiceBean BeanDefinition 没有注册,则进行注册
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
} else {
// ... 打印日志
}
}
// 生成 serviceBeanName, 规则是 ServiceBean:{接口路径}:{版本号}:{分组}
private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
ServiceBeanNameBuilder builder = create(interfaceClass, environment)
.group(serviceAnnotationAttributes.getString("group"))
.version(serviceAnnotationAttributes.getString("version"));
return builder.build();
}
我们这里需要关注如下代码,该方法构建了 ServiceBean 的 BeanDefinition:
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
其实现如下:
// 该方法返回的是 ServiceBean 的 BeanDefinition
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
// 1. 创建 BeanDefinition 创建器类
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
// 2. 开始为 BeanDefinition 添加 PropertyValue 属性
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
// 2.1 添加Service 注解属性,忽略 ignoreAttributeNames 中的属性
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
// References "ref" property to annotated-@Service Bean
// 2.2 添加 ref 属性。annotatedServiceBeanName是暴露的接口实现类,这里会将其封装为RuntimeBeanReference。
// 如 ProviderServiceImpl 为 Provider Service的实现类,这里会将其封装为 ref : new RuntimeBeanReference("providerServiceImpl")。
addPropertyReference(builder, "ref", annotatedServiceBeanName);
// 2.3 下面也是配置 propertyValue 属性,这里不再继续分析。
....
// 2.4 返回构建的 BeanDefinition
return builder.getBeanDefinition();
}
其中 ServiceBean#ref 保存调用的服务实例, 如下图:
注:
-
关于 RuntimeBeanReference :当我们需要动态注入Bean,并给该Bean的属性注入其他Bean时,比如在Mybatis和Spring的整合中,我们需要动态注入Mapper到spring容器中,而该Mapper如果需要执行SQL语句,还需要持有SqlSessionFactory的引用。但是我们注入时,可能对应的Bean还没有准备好,这时,我们就可以使用RuntimeBeanReference,以保持对实际Bean的引用。在Spring处理依赖关系时,最终会将该引用替换成实际生成的Bean对象。例如:
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
-
关于 ServiceBean,其 结构如下图:
这里我们需要注意 ServiceBean 是 AbstractConfig 的子类。我们这里需要看一下AbstractConfig#addIntoConfigManager 方法:@PostConstruct public void addIntoConfigManager() { // 将当前类添加到 ConfigManager 的配置中 ApplicationModel.getConfigManager().addConfig(this); }
AbstractConfig#addIntoConfigManager 方法被 @PostConstruct 注解修饰,也即是说,每个 ServiceBean 在创建时就会通过 AbstractConfig#addIntoConfigManager 将自身添加到 ConfigManager 的配置中(确切的说是保存到了 ConfigManager#configsCache 中缓存了起来)。在之后 DubboBootstrap 进行服务暴露时会使用到,具体我们后文再详解。
这里我们可以看到 ServiceBean 中包含了 Ref (提供服务的对象实例)、version (服务版本号)、group(服务分组) 等信息,这一步完成 ServiceBean的 BeanDefinition 后,Spring在后续的流程会创建对应的实例对象,Dubbo的服务暴露则是通过 这些ServiceBean 实例来完成。
1.4 总结
我们这里总结一下 ServiceAnnotationBeanPostProcessor 的作用:
-
在 Spring容器启动时,会触发
BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
方法,所以在 Spring 容器启动时会触发ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
方法。关于BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
方法,如有需要详参 Spring源码分析二:BeanFactoryPostProcessor 的处理 -
ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
中首先会判断DubboBootstrapApplicationListener
的BeanDefinition
是否注册,如果没有,则注册一个DubboBootstrapApplicationListener
的BeanDefinition
。在 Spring中,一个 Bean如果被创建,首先会创建其 BeanDefinition ,之后再根据BeanDefinition 创建真正的 Bean实例。这里的过程是创建BeanDefinition。 -
随后
ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
会解析出配置中的 dubbo.scan 配置路径,扫描指定路径下的所有bean,如果 bean被org.apache.dubbo.config.annotation.Service 和 com.alibaba.dubbo.config.annotation.Service
注解修饰,则会为其创建对应的 ServiceBean 的 BeanDefinition,并注册到容器中。这里需要注意,这里创建的BeanDefinition 代表的类型是 ServiceBean。假设此时 DemoService 被 @Service 注解修饰,这里会创建一个 ServiceBean 的 BeanDefinition,而这个 ServiceBean 中保存了 DemoService 服务暴露的相关信息,如 超时时间、版本号、具体服务实现类等。
2. DubboBootstrapApplicationListener
上面我们提到了 ServiceAnnotationBeanPostProcessor 中会注册DubboBootstrapApplicationListener 的 BeanDefinition 到容器中。
DubboBootstrapApplicationListener 的作用是监听刷新事件,用来监听Spring容器刷新结束或关闭事件,当Spring容器刷新结束后开始准备暴露dubbo服务,当容器关闭时则准备销毁 dubbo 服务。其实现如下:
public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
implements Ordered {
private final DubboBootstrap dubboBootstrap;
public DubboBootstrapApplicationListener() {
this.dubboBootstrap = DubboBootstrap.getInstance();
}
@Override
public void onApplicationContextEvent(ApplicationContextEvent event) {
// Springboot 容器刷新结束会发送此事件
if (event instanceof ContextRefreshedEvent) {
// 执行容器刷新
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
// 容器关闭触发
onContextClosedEvent((ContextClosedEvent) event);
}
}
// 启动 Dubbo服务
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
// 销毁 Dubbo服务
private void onContextClosedEvent(ContextClosedEvent event) {
dubboBootstrap.stop();
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE;
}
}
这里我们可以看到 DubboBootstrapApplicationListener 会监听 Spring 事件。
当 Spring容器刷新结束后会发送 ContextRefreshedEvent事件,而DubboBootstrapApplicationListener 接收到该事件后会通过 DubboBootstrap#start 来暴露服务。同理Spring容器销毁时发送 ContextClosedEvent事件,DubboBootstrapApplicationListener 则会通过 DubboBootstrap#stop 来停止 Dubbo 服务的暴露。
3. 总结
我们简单总结 :ServiceAnnotationBeanPostProcessor 在 容器进行 Bean注册时 会为被@Service 修饰的类创建 BeanDefinition 和对应的 ServiceBean 的 BeanDefinition 注册到容器中。Spring会根据每个 BeanDefinition 创建对应的类实例,而 ServiceBean 在创建时会通过 AbstractConfig#addIntoConfigManager 将自身添加到 ConfigManager 中。Spring 容器刷新结束后会发送 ContextRefreshedEvent 事件从而触发 DubboBootstrapApplicationListener#onApplicationContextEvent 事件,在此会调用 DubboBootstrap#start 方法,DubboBootstrap#start 会完成整个服务暴露的过程。
四、消费者的启动流程
上面我们以服务提供者的视角看了一下 Springboot启动后,Dubbo服务是如何启动的。下面我们来看一看服务消费者是如何引用的Dubbo服务。(我们这里以 @Reference 注解的方式引用 Dubbo服务为例)
对于提供者, Dubbo提供了ServiceAnnotationBeanPostProcessor 类处理 @Service,对于消费者则是提供了 ReferenceAnnotationBeanPostProcessor 来处理 @Reference 注解。
ReferenceAnnotationBeanPostProcessor 的结构如下:
这里需要注意的是 ServiceAnnotationBeanPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,用于注册 BeanDefinition,而 ReferenceAnnotationBeanPostProcessor 实现了 AbstractAnnotationBeanPostProcessor,参与了 Bean的创建过程。
1. ReferenceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor 是 Dubbo 提供用来处理 @Reference 注解的后处理器。
ReferenceAnnotationBeanPostProcessor 利用 Bean 后处理器的特性,在每个Bean创建时会判断其是否存在被 @Reference 注解 修饰的属性和方法,如果存在则进行注入。(注入的实例对象是其创建的Dubbo提供者的代理对象。)
关于 Bean 后处理器,如有需要详参:Spring源码分析衍生篇四:后处理器 BeanPostProcessor
下面我们具体来看 ReferenceAnnotationBeanPostProcessor 的逻辑。
1.1 构造函数
ReferenceAnnotationBeanPostProcessor 的构造函数如下:
public ReferenceAnnotationBeanPostProcessor() {
// org.apache.dubbo.config.annotation.Reference 和 com.alibaba.dubbo.config.annotation.Reference 注解
super(Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}
super调用其父类 AbstractAnnotationBeanPostProcessor 的构造函数,如下:
public AbstractAnnotationBeanPostProcessor(Class<? extends Annotation>... annotationTypes) {
Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
this.annotationTypes = annotationTypes;
}
可以看到,这里会将 ReferenceAnnotationBeanPostProcessor#annotationTypes
被赋值为 org.apache.dubbo.config.annotation.Reference
和 com.alibaba.dubbo.config.annotation.Reference
。
当 Bean创建时,会遍历其所有的属性和方法,判断是否被 ReferenceAnnotationBeanPostProcessor#annotationTypes
修饰,即是否被 org.apache.dubbo.config.annotation.Reference
和 com.alibaba.dubbo.config.annotation.Reference
修饰。
1.2 ReferenceAnnotationBeanPostProcessor#postProcessPropertyValues
在 Spring 容器中,Bean在创建成功后,会通过 AbstractAutowireCapableBeanFactory#populateBean 方法调用 InstantiationAwareBeanPostProcessor#postProcessProperties 和 InstantiationAwareBeanPostProcessor#postProcessPropertyValues 来填充 Bean的属性。
关于Spring Bean 创建的逻辑,如有需要详参 Spring源码分析七:bean的属性注入⑤ - populateBean
而对于 ReferenceAnnotationBeanPostProcessor, InstantiationAwareBeanPostProcessor#postProcessProperties 方法并没有实现,而InstantiationAwareBeanPostProcessor#postProcessPropertyValues 的实现在其父类 AbstractAnnotationBeanPostProcessor中,如下:
// AbstractAnnotationBeanPostProcessor#postProcessPropertyValues
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
// 1. 获取需要依赖注入的元数据信息
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
// 2. 进行属性注入
metadata.inject(bean, beanName, pvs);
} catch (...) {
... 抛出异常
}
return pvs;
}
这里的逻辑比较简单:
- 挑选出被
ReferenceAnnotationBeanPostProcessor#annotationTypes
修饰的属性或方法,并封装成 InjectionMetadata。 - 调用 InjectionMetadata#inject 来进行属性注入。
下面我们来具体看
1.2.1 AbstractAnnotationBeanPostProcessor#findInjectionMetadata
AbstractAnnotationBeanPostProcessor#findInjectionMetadata 方法挑选出被 ReferenceAnnotationBeanPostProcessor#annotationTypes
修饰的属性或方法,并封装成 InjectionMetadata。其实现如下:
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
// 尝试从缓存中获取
AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 如果需要刷新 : metadata == null || metadata.needsRefresh(clazz)
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
// 进行刷新操作,清除后重新创建
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
// 创建 元数据
metadata = buildAnnotatedMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError err) {
... 抛出异常
}
}
}
}
return metadata;
}
通过上面的代码我们可以发现其关键逻辑在于 metadata = buildAnnotatedMetadata(clazz);
,实现如下:
// 创建元数据
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
// 寻找被指定注解修饰的属性
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
// 寻找被指定注解修饰的方法
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
// 返回
return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
// 对属性的筛选
private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {
final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement>();
// 遍历当前 Bean的所有方法
ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// 获取注解类型
for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
// 获取注解属性
AnnotationAttributes attributes = getAnnotationAttributes(field, annotationType, getEnvironment(), true, true);
// 如果属性不为空则说明当前属性被指定注解修饰
if (attributes != null) {
// 属性为静态则直接返回
if (Modifier.isStatic(field.getModifiers())) {
return;
}
// 添加属性集合中
elements.add(new AnnotatedFieldElement(field, attributes));
}
}
}
});
return elements;
}
// 对 方法 的筛选和对属性的基本类型,
private List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {
final List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement>();
// 遍历当前 Bean的所有方法
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = findBridgedMethod(method);
if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
// 筛选被 指定注解修饰的方法
AnnotationAttributes attributes = getAnnotationAttributes(bridgedMethod, annotationType, getEnvironment(), true, true);
if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
// 筛选非静态
if (Modifier.isStatic(method.getModifiers())) {
return;
}
// 方法至少要有一个入参,这里打日志警告
if (method.getParameterTypes().length == 0) {
... 打印日志
}
// 获取方法表述信息
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
// 保存到集合中
elements.add(new AnnotatedMethodElement(method, pd, attributes));
}
}
}
});
return elements;
}
protected final Class<? extends Annotation>[] getAnnotationTypes() {
return annotationTypes;
}
上面我们可以看到,如果当前 Bean 有属性或方法被 ReferenceAnnotationBeanPostProcessor#annotationTypes
修饰,则认为当前属性或方法需要被注入。
而 ReferenceAnnotationBeanPostProcessor#annotationTypes 的值则是在构造函数中赋值的 org.apache.dubbo.config.annotation.Reference
和 com.alibaba.dubbo.config.annotation.Reference
。
因此,我们可以简单总结 AbstractAnnotationBeanPostProcessor#findInjectionMetadata
的作用是将 Bean 中被 org.apache.dubbo.config.annotation.Reference
和 com.alibaba.dubbo.config.annotation.Reference
修饰的属性或方法筛选出来并封装成 InjectionMetadata。
1.2.2 InjectionMetadata#inject
InjectionMetadata#inject 的作用是执行注入操作。其实现如下:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
// 执行元素注入操作,这里会调用 AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement 或 AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement
element.inject(target, beanName, pvs);
}
}
}
这里需要注意:
-
被 @Reference 注解修饰的 属性被封装成AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement 类型。其实现如下:
@Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { // 获取注入类型 Class<?> injectedType = field.getType(); // 获取注入的对象实例 Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); // 设置权限 ReflectionUtils.makeAccessible(field); // 反射赋值 field.set(bean, injectedObject); }
-
被 @Reference 注解修饰的 方法被封装成AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement 类型。其实现如下
@Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { // 获取注入类型 Class<?> injectedType = pd.getPropertyType(); // 获取注入的对象实例 Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); // 设置权限 ReflectionUtils.makeAccessible(method); // 反射调用 method.invoke(bean, injectedObject); }
上面我们看到,注入的实例是通过 AbstractAnnotationBeanPostProcessor#getInjectedObject来获取,下面我们来看看 其具体实现。
1.2.3 AbstractAnnotationBeanPostProcessor#getInjectedObject
AbstractAnnotationBeanPostProcessor#getInjectedObject 返回了一个服务提供者的代理对象,其实现如下:
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
// 获取缓存 key
String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
// 尝试从缓存中获取
Object injectedObject = injectedObjectsCache.get(cacheKey);
// 缓存没有命中
if (injectedObject == null) {
// 获取要注入的 Bean
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
// 放入缓存中
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
AbstractAnnotationBeanPostProcessor#doGetInjectedBean是一个抽象方法,在 ReferenceAnnotationBeanPostProcessor 中实现了该方法,如下:
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
// 以 @Service 注解规则生成的 ServiceBean BeanName
// ServiceBean:com.kingfish.service.ProviderService:1.0.0:spring
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
// 以 @Reference 注解规则生成的 BeanName :如果指定id,则返回id,否则按照规则:@Reference(属性key=value,...) 接口名 生成
// 如:@Reference(group=spring,version=1.0.0) com.kingfish.service.ProviderService
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
// 为当前引用创建一个 ReferenceBean 对象(如果需要)
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
// ReferenceBean 注册到容器中
registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);
// 缓存 ReferenceBean
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 创建Dubbo 服务引用服务的代理对象。
return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
}
这里可以看到通过 getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
方法创建了一个 Dubbo提供者的代理对象并返回。
关于 Dubbo 消费者的创建过程,之前文章有过对 2.7.0版本的介绍,如有需要详参:Dubbo源码分析:全集整理 ,关于2.7.5版本的差异,后续会有文章来对比。
2. 总结
我们简单总结:消费者服务在启动后, 每个 Bean 创建时都会调用 InstantiationAwareBeanPostProcessor#postProcessProperties 和 InstantiationAwareBeanPostProcessor#postProcessPropertyValues 来填充属性。而 ReferenceAnnotationBeanPostProcessor 这里则是通过反射寻找被 @Reference 注解修饰的属性或方法,随后创建对应的Dubbo 代理类赋值。
以上:内容部分参考
《深度剖析Apache Dubbo 核心技术内幕》
https://blog.csdn.net/lichuangcsdn/article/details/89931460
https://blog.csdn.net/zhaodongchao1992/article/details/103475912
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正