Files
JavaYouth/docs/dubbo-sourcecode-v1/04.Dubbo源码系列V1-Dubbo第四节-Spring与Dubbo整合原理与源码分析.md
2023-01-06 00:10:22 +08:00

1890 lines
76 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 04.Dubbo源码系列V1-Dubbo第四节-Spring与Dubbo整合原理与源码分析
tags:
- Dubbo
- rpc
categories:
- rpc
- Dubbo源码系列v1
keywords: Dubborpc
description: Spring与Dubbo整合原理与源码分析
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/dubbo.png'
abbrlink: 796f395d
date: 2021-10-06 13:21:58
---
## 第四节: Spring与Dubbo整合原理与源码分析
### 笔记更新地址:
[https://www.yuque.com/books/share/f2394ae6-381b-4f44-819e-c231b39c1497](https://www.yuque.com/books/share/f2394ae6-381b-4f44-819e-c231b39c1497?#)密码kyys 《Dubbo笔记》
### 整体架构和流程
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/04_di_si_jie/Spring整合Dubbo架构图.png"/>
#### 处理@Service
1. Dubbo的@Service注解Spring的@Service注解重名了dubbo在2.7版本之后改成了@DubboService注解
> 在Dubbo的文章中如果不是特别说明@Service注解均为Dubbo的注解
2. Dubbo在处理@Service注解时会生成两个对象看上面的图DemoServiceImpl这个是给Spring容器生成的意思就是@Service注解兼具了Spring@Service注解的功能。同时@Serivce注解会再生成一个ServiceBean类型的对象这个对象会做一些事情比如将对应的服务类注册到注册中心将service服务进行分组分版本控制超时权重等等。@Service注解上面写的参数都是由ServiceBean类型的对象来承接。当调用ServiceBean类型里面的export方法就可以控制服务的注册。
3. ServiceBean的父类里有一个ref属性指向这个服务的实现类
#### 处理Properties文件
```properties
# 这个会被解析成ApplicationConfig对象
dubbo.application.name=dubbo-demo-provider1-application
dubbo.application.logger=log4j
dubbo.application.timeout=3000
# 这个会被解析成ProtocolConfig
dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20880
dubbo.protocols.p1.host=0.0.0.0
dubbo.protocols.p2.name=dubbo
dubbo.protocols.p2.port=20881
dubbo.protocols.p2.host=0.0.0.0
# 这个会被解析成RegistrieConfig
dubbo.registries.r1.address=zookeeper://127.0.0.1:2181
dubbo.registries.r1.timeout=3000
# 等等
```
这些xxxConfig最后都会赋值给ServiceBean里相应的属性
### 实例解析
应用启动类与配置
```java
public class Application {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
System.in.read();
}
@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")
@PropertySource("classpath:/spring/dubbo-provider.properties")
static class ProviderConfiguration {
}
}
```
应用配置类为ProviderConfiguration, 在配置上有两个比较重要的注解
1. @PropertySource表示将dubbo-provider.properties中的配置项添加到Spring容器中可以通过@Value的方式获取到配置项中的值
2. @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")表示对指定包下的类进行扫描,扫描@Service与@Reference注解,并且进行处理
### @EnableDubbo
在EnableDubbo注解上有另外两个注解也是研究Dubbo最重要的两个注解
1. @EnableDubboConfig
2. @DubboComponentScan
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
boolean multiple() default true;
}
//解析properties文件 ===> xxxConfig
```
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
//解析@Service @Refrence注解
```
注意两个注解中对应的@Import注解所导入的类
1. DubboConfigConfigurationRegistrar
2. DubboComponentScanRegistrar
Spring在启动时会解析这两个注解并且执行对应的Registrar类中的registerBeanDefinitions方法这是Spring中提供的扩展功能。
### DubboConfigConfigurationRegistrar
```java
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("执行DubboConfigConfigurationRegistrar");
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
boolean multiple = attributes.getBoolean("multiple"); // 拿EnableDubboConfig里的默认值
// Single Config Bindings
registerBeans(registry, DubboConfigConfiguration.Single.class);
// 默认为true
if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
registerBeans(registry, DubboConfigConfiguration.Multiple.class);
}
}
}
```
#### 流程
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/04_di_si_jie/Spring整合Dubbo只properties文件解析流程.png"/>
Spring启动时会调用DubboConfigConfigurationRegistrar的registerBeanDefinitions方法该方法是利用Spring中的AnnotatedBeanDefinitionReader来读取
1. DubboConfigConfiguration.Single.**class**
2. DubboConfigConfiguration.Multiple.**class**
这两个类上的注解。
```java
public class DubboConfigConfiguration {
/**
* Single Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
@EnableDubboConfigBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.metrics", type = MetricsConfig.class)
})
public static class Single {
}
/**
* Multiple Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
@EnableDubboConfigBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
})
public static class Multiple {
}
}
```
大概意思就是什么前缀的注解,对应解析到哪个类的对象里。
这两个类主要用到的就是@EnableDubboConfigBindings注解
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboConfigBindingsRegistrar.class)
public @interface EnableDubboConfigBindings {
/**
* The value of {@link EnableDubboConfigBindings}
*
* @return non-null
*/
EnableDubboConfigBinding[] value();
}
```
@EnableDubboConfigBindings注解上也有一个@Import注解导入的是DubboConfigBindingsRegistrar.**class**。该类会获取@EnableDubboConfigBindings注解中的value,也就是多个@EnableDubboConfigBinding注解然后利用DubboConfigBindingRegistrar去处理这些@EnableDubboConfigBinding注解
#### DubboConfigBindingRegistrar
##### 此类总结
此类中的主要方法是registerDubboConfigBeans()方法主要功能就是获取用户所设置的properties文件中的内容对Properties文件进行解析根据Properties文件的每个配置项的前缀、参数名、参数值生成对应的BeanDefinition。
比如:
```java
dubbo.application.name=dubbo-demo-provider1-application
dubbo.application.logger=log4j
```
前缀为"dubbo.application"的配置项会生成一个ApplicationConfig类型的BeanDefinition并且name和logger属性为对应的值。
再比如:
```java
dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20880
dubbo.protocols.p1.host=0.0.0.0
dubbo.protocols.p2.name=dubbo
dubbo.protocols.p2.port=20881
dubbo.protocols.p2.host=0.0.0.0
```
比如前缀为"dubbo.protocols"的配置项,会生成**两**个ProtocolConfig类型的BeanDefinition两个BeanDefinition的beanName分别为p1和p2。
并且还会针对生成的每个BeanDefinition生成一个和它一对一绑定的BeanPostProcessor类型为DubboConfigBindingBeanPostProcessor.**class**。
##### 代码注释
> 整个类的代码注释
```java
/**
* {@link AbstractConfig Dubbo Config} binding Bean registrar
*
* @see EnableDubboConfigBinding
* @see DubboConfigBindingBeanPostProcessor
* @since 2.5.8
*/
public class DubboConfigBindingRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private final Log log = LogFactory.getLog(getClass());
private ConfigurableEnvironment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("执行DubboConfigBindingRegistrar");
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBinding.class.getName()));
registerBeanDefinitions(attributes, registry);
}
protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {
// prefix = "dubbo.application"
String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));
// type = ApplicationConfig.class
Class<? extends AbstractConfig> configClass = attributes.getClass("type");
boolean multiple = attributes.getBoolean("multiple");
registerDubboConfigBeans(prefix, configClass, multiple, registry);
}
private void registerDubboConfigBeans(String prefix,
Class<? extends AbstractConfig> configClass,
boolean multiple,
BeanDefinitionRegistry registry) {
// 从properties文件中根据前缀拿对应的配置项比如根据dubbo.application前缀
// 就可以拿到:
// dubbo.application.name=dubbo-demo-provider-application
// dubbo.application.logger=log4j
Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);
// 如果没有相关的配置项则不需要注册BeanDefinition
if (CollectionUtils.isEmpty(properties)) {
if (log.isDebugEnabled()) {
log.debug("There is no property for binding to dubbo config class [" + configClass.getName()
+ "] within prefix [" + prefix + "]");
}
return;
}
// 根据配置项生成beanNames为什么会有多个
// 普通情况一个dubbo.application前缀对应一个ApplicationConfig类型的Bean
// 特殊情况下比如dubbo.protocols对应了
// dubbo.protocols.p1.name=dubbo
// dubbo.protocols.p1.port=20880
// dubbo.protocols.p1.host=0.0.0.0
// dubbo.protocols.p2.name=http
// dubbo.protocols.p2.port=8082
// dubbo.protocols.p2.host=0.0.0.0
// 那么就需要对应两个ProtocolConfig类型的Bean那么就需要两个beanName:p1和p2
// 这里就是multiple为true或false的区别名字的区别根据multiple用来判断是否从配置项中获取beanName
// 如果multiple为false则看有没有配置id属性如果没有配置则自动生成一个beanName.
Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
Collections.singleton(resolveSingleBeanName(properties, configClass, registry));
for (String beanName : beanNames) {
// 为每个beanName,注册一个空的BeanDefinition
registerDubboConfigBean(beanName, configClass, registry);
// 为每个bean注册一个DubboConfigBindingBeanPostProcessor的Bean后置处理器
registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);
}
// 注册一个NamePropertyDefaultValueDubboConfigBeanCustomizer的bean
// 用来把某个XxConfig所对应的beanName设置到name属性中去
registerDubboConfigBeanCustomizers(registry);
}
private void registerDubboConfigBean(String beanName, Class<? extends AbstractConfig> configClass,
BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
registry.registerBeanDefinition(beanName, beanDefinition); // ApplicatinoConfig对象
if (log.isInfoEnabled()) {
log.info("The dubbo config bean definition [name : " + beanName + ", class : " + configClass.getName() +
"] has been registered.");
}
}
private void registerDubboConfigBindingBeanPostProcessor(String prefix, String beanName, boolean multiple,
BeanDefinitionRegistry registry) {
// 注册一个DubboConfigBindingBeanPostProcessor的Bean
// 每个XxConfig的Bean对应一个DubboConfigBindingBeanPostProcessor的Bean
// 比如一个ApplicationConfig对应一个DubboConfigBindingBeanPostProcessor
// 一个ProtocolConfig也会对应一个DubboConfigBindingBeanPostProcessor
// 在构造DubboConfigBindingBeanPostProcessor的时候会指定构造方法的值这样就可以区别开来了
Class<?> processorClass = DubboConfigBindingBeanPostProcessor.class;
BeanDefinitionBuilder builder = rootBeanDefinition(processorClass);
// 真实的前缀比如dubbo.registries.r2
String actualPrefix = multiple ? normalizePrefix(prefix) + beanName : prefix;
// 添加两个构造方法参数值所以会调用DubboConfigBindingBeanPostProcessor的两个参数的构造方法
builder.addConstructorArgValue(actualPrefix).addConstructorArgValue(beanName);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerWithGeneratedName(beanDefinition, registry);
if (log.isInfoEnabled()) {
log.info("The BeanPostProcessor bean definition [" + processorClass.getName()
+ "] for dubbo config bean [name : " + beanName + "] has been registered.");
}
}
private void registerDubboConfigBeanCustomizers(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, BEAN_NAME, NamePropertyDefaultValueDubboConfigBeanCustomizer.class);
}
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
this.environment = (ConfigurableEnvironment) environment;
}
private Set<String> resolveMultipleBeanNames(Map<String, Object> properties) {
Set<String> beanNames = new LinkedHashSet<String>();
// 比如dubbo.protocols.p1.name=dubbo的propertyName为p1.name
for (String propertyName : properties.keySet()) {
// propertyName为p1.name
int index = propertyName.indexOf(".");
if (index > 0) {
// 截取beanName名字为p1
String beanName = propertyName.substring(0, index);
beanNames.add(beanName);
}
}
return beanNames;
}
private String resolveSingleBeanName(Map<String, Object> properties, Class<? extends AbstractConfig> configClass,
BeanDefinitionRegistry registry) {
// 配置了dubbo.application.id=appl那么appl就是beanName
String beanName = (String) properties.get("id");
// 如果beanName为null则会进入if分支由spring自动生成一个beanName,比如org.apache.dubbo.config.ApplicationConfig#0
if (!StringUtils.hasText(beanName)) {
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
beanName = BeanDefinitionReaderUtils.generateBeanName(builder.getRawBeanDefinition(), registry);
}
return beanName;
}
}
```
#### DubboConfigBindingBeanPostProcessor
DubboConfigBindingBeanPostProcessor是一个BeanPostProcessor在Spring启动过程中会针对所有的Bean对象进行后置加工但是在DubboConfigBindingBeanPostProcessor中有如下判断
```java
if (this.beanName.equals(beanName) && bean instanceof AbstractConfig)
```
所以DubboConfigBindingBeanPostProcessor并不会处理Spring容器中的所有Bean它只会处理上文由Dubbo所生成的Bean对象。
并且在afterPropertiesSet()方法中会先创建一个DefaultDubboConfigBinder。
##### 代码注释
> 只留了关键性的代码注释,其余省略
```java
/**
* Dubbo Config Binding {@link BeanPostProcessor}
*
* @see EnableDubboConfigBinding
* @see DubboConfigBindingRegistrar
* @since 2.5.8
*/
public class DubboConfigBindingBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, InitializingBean
, BeanDefinitionRegistryPostProcessor {
private final Log log = LogFactory.getLog(getClass());
/**
* The prefix of Configuration Properties
*/
private final String prefix;
/**
* Binding Bean Name
*/
private final String beanName;
private DubboConfigBinder dubboConfigBinder;
private ApplicationContext applicationContext;
private BeanDefinitionRegistry beanDefinitionRegistry;
private boolean ignoreUnknownFields = true;
private boolean ignoreInvalidFields = true;
private List<DubboConfigBeanCustomizer> configBeanCustomizers = Collections.emptyList();
/**
* @param prefix the prefix of Configuration Properties
* @param beanName the binding Bean Name
*/
public DubboConfigBindingBeanPostProcessor(String prefix, String beanName) {
Assert.notNull(prefix, "The prefix of Configuration Properties must not be null");
Assert.notNull(beanName, "The name of bean must not be null");
this.prefix = prefix;
this.beanName = beanName;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
/*
1.每个XxConfig对应一个BeanPostProcessor所以每个DubboConfigBindingBeanPostProcessor只处理对应的beanName
2.阿里这里的代码写的很不好没必要为每一个Bean都生成一个BeanPostProcessor多余的加这个if判断每一个Bean
都会经过Dubbo生成的BeanPostProcessor不停的if判断直到找到自己的BeanPostProcessor。还好Dubbo后面的版本
把这里改了。
3.看过Spring源码的应该知道Spring的BeanPostProcessor是所有对象公用的这种处理就比较好.
*/
if (this.beanName.equals(beanName) && bean instanceof AbstractConfig) {
AbstractConfig dubboConfig = (AbstractConfig) bean;
// 从properties文件中获取值并设置到dubboConfig对象中
bind(prefix, dubboConfig);
// 设置dubboConfig对象的name属性设置为beanName
customize(beanName, dubboConfig);
}
return bean;
}
private void bind(String prefix, AbstractConfig dubboConfig) {
dubboConfigBinder.bind(prefix, dubboConfig);
if (log.isInfoEnabled()) {
log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
"configuration properties : " + prefix);
}
}
private void customize(String beanName, AbstractConfig dubboConfig) {
for (DubboConfigBeanCustomizer customizer : configBeanCustomizers) {
customizer.customize(beanName, dubboConfig);
}
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AbstractConfig) {
// 添加别名id属性的值为别名
String id = ((AbstractConfig) bean).getId();
if (beanDefinitionRegistry != null && beanDefinitionRegistry instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanDefinitionRegistry;
if (!StringUtils.isBlank(id) && !factory.hasAlias(beanName, id)) {
beanDefinitionRegistry.registerAlias(beanName, id);
}
}
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//这里建议看看Spring源码
@Override
public void afterPropertiesSet() throws Exception {
initDubboConfigBinder(); // 创建DefaultDubboConfigBinder
initConfigBeanCustomizers();
}
private void initDubboConfigBinder() {
if (dubboConfigBinder == null) {
try {
// 先从Spring容器中获取DubboConfigBinder默认获取不到
dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class);
} catch (BeansException ignored) {
if (log.isDebugEnabled()) {
log.debug("DubboConfigBinder Bean can't be found in ApplicationContext.");
}
// Use Default implementation
// 生成一个默认的
dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment());
}
}
dubboConfigBinder.setIgnoreUnknownFields(ignoreUnknownFields);
dubboConfigBinder.setIgnoreInvalidFields(ignoreInvalidFields);
}
private void initConfigBeanCustomizers() {
// 得到之前创建了的NamePropertyDefaultValueDubboConfigBeanCustomizer
Collection<DubboConfigBeanCustomizer> configBeanCustomizers =
beansOfTypeIncludingAncestors(applicationContext, DubboConfigBeanCustomizer.class).values();
this.configBeanCustomizers = new ArrayList<>(configBeanCustomizers);
AnnotationAwareOrderComparator.sort(this.configBeanCustomizers);
}
/**
* Create {@link DubboConfigBinder} instance.
*
* @param environment
* @return {@link DefaultDubboConfigBinder}
*/
protected DubboConfigBinder createDubboConfigBinder(Environment environment) {
DefaultDubboConfigBinder defaultDubboConfigBinder = new DefaultDubboConfigBinder();
defaultDubboConfigBinder.setEnvironment(environment);
return defaultDubboConfigBinder;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.beanDefinitionRegistry == null) {
this.beanDefinitionRegistry = registry;
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//do nothing here
}
}
```
#### DefaultDubboConfigBinder
当某个AbstractConfig类型的Bean在经过DubboConfigBindingBeanPostProcessor处理时此时Bean对象中的属性是没有值的会利用DefaultDubboConfigBinder进行赋值。底层就是利用Spring中的DataBinder技术结合properties文件对对应的属性进行赋值。
对应一个AbstractConfig类型针对的其实是子类比如ApplicationConfig、RegistryConfig的Bean每个类都有一些属性而properties文件是一个key-value对所以实际上DataBinder就是将属性名和properties文件中的key进行匹配如果匹配成功则把value赋值给属性。具体DataBinder技术是如何工作的请自行学习不难
举个例子:
```java
dubbo.application.name=dubbo-demo-provider1-application
dubbo.application.logger=log4j
```
对于此配置它对应ApplicationConfig对象beanName是自动生成的所以最终ApplicationConfig对象的name属性的值为“dubbo-demo-provider1-application”logger属性的值为“log4j”。
对于
```java
dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20880
dubbo.protocols.p1.host=0.0.0.0
```
它对应ProtocolConfig对象beanName为p1,所以最终ProtocolConfig对象的name属性的值为“dubbo”port属性的值为20880host属性的值为“0.0.0.0”。
这样就完成了对properties文件的解析。
```java
public class DefaultDubboConfigBinder extends AbstractDubboConfigBinder {
@Override
public <C extends AbstractConfig> void bind(String prefix, C dubboConfig) {
DataBinder dataBinder = new DataBinder(dubboConfig);
// Set ignored*
dataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields());
dataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields());
// Get properties under specified prefix from PropertySources
// getPropertySources()会拿到由@PropertySource注入进来的properties文件中的内容
// 同时还包括当前java的所有环境变量包括手动通过-D添加的配置
Map<String, Object> properties = getSubProperties(getPropertySources(), prefix);
// Convert Map to MutablePropertyValues
MutablePropertyValues propertyValues = new MutablePropertyValues(properties);
// Bind
dataBinder.bind(propertyValues);
}
}
```
#### 总结
DubboConfigConfigurationRegistrar的主要作用就是对propties文件进行解析并根据不同的配置项项生成对应类型的Bean对象。
### DubboComponentScanRegistrar
DubboConfigConfigurationRegistrar的作用是向Spring容器中注册两个Bean:
1. ServiceAnnotationBeanPostProcessor
2. ReferenceAnnotationBeanPostProcessor
#### 代码注释
> 部分代码注释
```java
/**
* Dubbo {@link DubboComponentScan} Bean Registrar
*
* @see Service
* @see DubboComponentScan
* @see ImportBeanDefinitionRegistrar
* @see ServiceAnnotationBeanPostProcessor
* @see ReferenceAnnotationBeanPostProcessor
* @since 2.5.7
*/
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("执行DubboComponentScanRegistrar");
// 拿到DubboComponentScan注解所定义的包路径扫描该package下的类识别这些类上
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
// 注册ServiceAnnotationBeanPostProcessor一个Bean
// 实现了BeanDefinitionRegistryPostProcessor接口所以在Spring启动时会调用postProcessBeanDefinitionRegistry方法
// 该方法会进行扫描,扫描@Service注解了的类然后生成BeanDefinition会生成两个一个普通的bean一个ServiceBean后续的Spring周期中会生成Bean
// 在ServiceBean中会监听ContextRefreshedEvent事件一旦Spring启动完后就会进行服务导出
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// 注册ReferenceAnnotationBeanPostProcessor
// 实现了AnnotationInjectedBeanPostProcessor接口继而实现了InstantiationAwareBeanPostProcessorAdapter接口
// 所以Spring在启动时在对属性进行注入时会调用AnnotationInjectedBeanPostProcessor接口中的postProcessPropertyValues方法
// 在这个过程中会按照@Reference注解的信息去生成一个RefrenceBean对象
registerReferenceAnnotationBeanPostProcessor(registry);
}
/**
* Registers {@link ServiceAnnotationBeanPostProcessor}
*
* @param packagesToScan packages to scan without resolving placeholders
* @param registry {@link BeanDefinitionRegistry}
* @since 2.5.8
*/
private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 生成一个RootBeanDefinition对应的beanClass为ServiceAnnotationBeanPostProcessor.class
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
// 将包路径作为在构造ServiceAnnotationBeanPostProcessor时调用构造方法时的传入参数
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
/**
* Registers {@link ReferenceAnnotationBeanPostProcessor} into {@link BeanFactory}
*
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {
// Register @Reference Annotation Bean Processor
// 注册一个ReferenceAnnotationBeanPostProcessor做为beanReferenceAnnotationBeanPostProcessor是一个BeanPostProcessor
BeanRegistrar.registerInfrastructureBean(registry,
ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
}
//省略...
}
```
#### ServiceAnnotationBeanPostProcessor
> 主要就是处理@Service注解
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/04_di_si_jie/@Service注解处理流程.png"/>
ServiceAnnotationBeanPostProcessor是一个BeanDefinitionRegistryPostProcessor是用来注册BeanDefinition的。这个类的名字起的不太好。
它的主要作用是扫描Dubbo的@Service注解一旦扫描到某个@Service注解就把它以及被它注解的类当做一个Dubbo服务进行**服务导出**。
##### DubboClassPathBeanDefinitionScanner
DubboClassPathBeanDefinitionScanner是所Dubbo自定义的扫描器继承了Spring中的ClassPathBeanDefinitionScanner了。
DubboClassPathBeanDefinitionScanner相对于ClassPathBeanDefinitionScanner并没有做太多的改变只是把useDefaultFilters设置为了false主要是因为Dubbo中的@Service注解是Dubbo自定义的在这个注解上并没有用@Component注解因为Dubbo不是一定要结合Spring才能用所以为了能利用Spring的扫描逻辑需要把useDefaultFilters设置为false。
每扫描到一个@Service注解就会得到一个BeanDefinition这个BeanDefinition的beanClass属性就是具体的服务实现类。
如果仅仅只是这样这只是得到了一个Spring中的Bean对于Dubbo来说此时得到的Bean是一个**服务**,并且,还需要解析@Service注解的配置信息因为这些都是服务的参数信息所以在扫描完了之后会针对所得到的每个BeanDefinition都会**额外**的再生成一个**ServiceBean**类型的Bean对象。
##### ServiceBean
ServiceBean表示一个Dubbo服务它有一些参数比如
1. **ref表示服务的具体实现类**
2. **interface表示服务的接口**
3. **parameters表示服务的参数@Service注解中所配置的信息**
4. **application表示服务所属的应用**
5. **protocols表示服务所使用的协议**
6. **registries表示服务所要注册的注册中心**
**所以在扫描到一个@Service注解后其实会得到两个Bean:**
1. 一个就是服务实现类本身一个Bean对象
2. 一个就是对应的ServiceBean类型的一个Bean对象
并且需要注意的是ServiceBean实现了ApplicationListener接口所以当Spring启动完成后会触发onApplicationEvent()方法的调用,而在这个方法内会调用**export()**,这个方法就是**服务导出的入口方法**。
> 关于RuntimeBeanReference参考[https://www.yuque.com/renyong-jmovm/ufz328/gbwvk7](https://www.yuque.com/renyong-jmovm/ufz328/gbwvk7)。
##### 代码注释
> 部分代码注释
```java
/**
* {@link Service} Annotation
* {@link BeanDefinitionRegistryPostProcessor Bean Definition Registry Post Processor}
*
* @since 2.5.8
*/
public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Set<String> packagesToScan;
private Environment environment;
private ResourceLoader resourceLoader;
private ClassLoader classLoader;
public ServiceAnnotationBeanPostProcessor(String... packagesToScan) {
this(Arrays.asList(packagesToScan));
}
public ServiceAnnotationBeanPostProcessor(Collection<String> packagesToScan) {
this(new LinkedHashSet<>(packagesToScan));
}
public ServiceAnnotationBeanPostProcessor(Set<String> packagesToScan) {
this.packagesToScan = packagesToScan;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 扫描包进行Bean注册
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
/**
* Registers Beans whose classes was annotated {@link Service}
*
* @param packagesToScan The base packages to scan
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 扫描被Service注解标注的类先成为Spring里的bean也就是dubbo服务的实现类
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
/**
* Add the compatibility for legacy Dubbo's @Service
*
* The issue : https://github.com/apache/dubbo/issues/4330
* @since 2.7.3
*/
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
// 扫描Dubbo自定义的@Service注解
scanner.scan(packageToScan);
// 查找被@Service注解的类的BeanDefinition无论这个类有没有被@ComponentScan注解标注了
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
// beanDefinitionHolders装的就是dubbo服务的实现类存在了Spring容器里
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
// 扫描到BeanDefinition开始处理它准备生成ServiceBean
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
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 + "]");
}
}
}
}
/**
* Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition}
*
* @param beanDefinitionHolder
* @param registry
* @param scanner
* @see ServiceBean
* @see BeanDefinition
*/
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
// 服务实现类
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// @Service注解
Annotation service = findServiceAnnotation(beanClass);
/**
* The {@link AnnotationAttributes} of @Service annotation
*/
// @Service注解上的信息
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// 服务实现类对应的接口
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
// 服务实现类对应的bean的名字比如demoServiceImpl
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 生成一个ServiceBean
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
// 把ServiceBean注册进去对应的beanName为ServiceBean:org.apache.dubbo.demo.DemoService
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
if (logger.isInfoEnabled()) {
logger.info("The BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean has been registered with name : " + beanName);
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean[ bean name : " + beanName +
"] was be found , Did @DubboComponentScan scan to same package in many times?");
}
}
}
/**
* Generates the bean name of {@link ServiceBean}
*
* @param serviceAnnotationAttributes
* @param interfaceClass the class of interface annotated {@link Service}
* @return ServiceBean@interfaceClassName#annotatedServiceBeanName
* @since 2.7.3
*/
private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
ServiceBeanNameBuilder builder = create(interfaceClass, environment)
.group(serviceAnnotationAttributes.getString("group"))
.version(serviceAnnotationAttributes.getString("version"));
return builder.build();
}
/**
* Build the {@link AbstractBeanDefinition Bean Definition}
*
* @param serviceAnnotation
* @param serviceAnnotationAttributes
* @param interfaceClass
* @param annotatedServiceBeanName
* @return
* @since 2.7.3
*/
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
// 生成一个ServiceBean对应的BeanDefinition
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
/*
1.先忽略这些比较特殊的属性,为什么要忽略呢?
2.其它不忽略的属性,在@Service注解上写的是字符串形式ServiceBean里也是字符串类型所以可以直接赋值
3.下面的这些特殊属性比如protocol在注解上写的是@Service(protocol="p1")这样的字符串但是在ServiceBean里
对应的是ProtocolConfig类型的属性不可能把字符串赋值给这类型。所以先忽略后面可以看到单独处理了
*/
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
// 把@Service注解中的参数值赋值给ServiceBean的属性
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
// References "ref" property to annotated-@Service Bean
// ref属性赋值为另外一个bean, 对应的就是被@Service注解的服务实现类对应的bean
addPropertyReference(builder, "ref", annotatedServiceBeanName);
// Set interface
builder.addPropertyValue("interface", interfaceClass.getName());
// Convert parameters into map
builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
// 配置了methods属性则给ServiceBean对应的methods属性赋值
// Add methods parameters
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
/**
* Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
*/
String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
if (StringUtils.hasText(providerConfigBeanName)) {
addPropertyReference(builder, "provider", providerConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
*/
String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
if (StringUtils.hasText(monitorConfigBeanName)) {
addPropertyReference(builder, "monitor", monitorConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
*/
String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
if (StringUtils.hasText(applicationConfigBeanName)) {
addPropertyReference(builder, "application", applicationConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
*/
String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
if (StringUtils.hasText(moduleConfigBeanName)) {
addPropertyReference(builder, "module", moduleConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
* 获取注解上配置的注册中心的beanName
*/
String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");
List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
if (!registryRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("registries", registryRuntimeBeanReferences);
}
/**
* Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
*/
String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");
List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
if (!protocolRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
}
return builder.getBeanDefinition();
}
}
```
#### ReferenceAnnotationBeanPostProcessor
> 处理@Reference注解
<img src="https://npm.elemecdn.com/youthlql@1.0.4/rpc/dubbo/v1/04_di_si_jie/@Reference注解处理流程.png"/>
##### 总结
ReferenceAnnotationBeanPostProcessor是处理@Reference注解的。
ReferenceAnnotationBeanPostProcessor的父类是AnnotationInjectedBeanPostProcessor是一个InstantiationAwareBeanPostProcessorAdapter是一个BeanPostProcessor。
Spring在对Bean进行依赖注入时会调用AnnotationInjectedBeanPostProcessor的postProcessPropertyValues()方法来给某个Bean按照ReferenceAnnotationBeanPostProcessor的逻辑进行依赖注入。
在注入之前会查找注入点,被@Reference注解的属性或方法都是注入点。
针对某个Bean找到所有注入点之后就会进行注入了注入就是给属性或给set方法赋值但是在赋值之前得先得到一个值此时就会调用ReferenceAnnotationBeanPostProcessor的**doGetInjectedBean()**方法来得到一个对象而这个对象的构造就比较复杂了因为对于Dubbo来说注入给某个属性的应该是当前这个**属性所对应的服务接口的代理对象**。
但是在生成这个代理对象之前,还要考虑问题:
1. 当前所需要引入的这个服务,是不是在本地就存在(就是当前项目)不存在则要把按Dubbo的逻辑生成一个代理对象
2. 当前所需要引入的这个服务,是不是已经被引入过了(是不是已经生成过代理对象了),如果是应该是不用再重复去生成了。
**首先如何判断当前所引入的服务是本地的一个服务(就是当前应用自己所提供的服务)。**
我们前面提到Dubbo通过@Service来提供一个服务并且会生成两个Bean
1. 一个服务实现类本身Bean
2. 一个ServiceBean类型的Bean这个Bean的名字是这么生成的
```java
private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
ServiceBeanNameBuilder builder = create(interfaceClass, environment)
.group(serviceAnnotationAttributes.getString("group"))
.version(serviceAnnotationAttributes.getString("version"));
return builder.build();
}
```
是通过接口类型+group+version来作为ServiceBean类型Bean的名字的。
```java
ServiceBean:org.apache.dubbo.demo.DemoService:group:version
```
所以现在对于服务引入,也应该提前根据@Reference注解中的信息和属性接口类型去判断一下当前Spring容器中是否存在对应的ServiceBean对象如果存在则直接取出ServiceBean对象的ref属性所对应的对象作为要注入的结果。
**然后如何判断当前所引入的这个服务是否已经被引入过了****(是不是已经生成过代理对象了)。**
这就需要在第一次引入某个服务后生成代理对象后进行缓存记录一下。Dubbo中是这么做的:
1. 首先根据@Reference注解的所有信息+属性接口类型生成一个**字符串**
2. 然后@Reference注解的所有信息+属性接口类型生成一个ReferenceBean对象**ReferenceBean对象中的get方法可以得到一个Dubbo生成的代理对象可以理解为服务引入的入口方法**
3. 把字符串作为beanNameReferenceBean对象作为bean注册到Spring容器中同时也会放入**referenceBeanCache**中。
有了这些逻辑,@Reference注解服务引入的过程是这样的
1. 得到当前所引入服务对应的ServiceBean的beanName源码中叫referencedBeanName
2. 根据@Reference注解的所有信息+属性接口类型得到一个referenceBeanName
3. 根据referenceBeanName从**referenceBeanCach**e获取对应的ReferenceBean如果没有则创建一个ReferenceBean
4. 根据referencedBeanNameServiceBean的beanName判断Spring容器中是否存在该bean如果存在则给ref属性所对应的bean取一个别名别名为referenceBeanName。
1. 如果Spring容器中不存在referencedBeanName对应的bean则判断容器中是否存在referenceBeanName所对应的Bean如果不存在则将创建出来的ReferenceBean注册到Spring容器中**此处这么做就支持了可以通过@Autowired注解也可以使用服务了****ReferenceBean是一个FactoryBean**
5. 如果referencedBeanName存在对应的Bean则额外生成一个代理对象代理对象的InvocationHandler会缓存在**localReferenceBeanInvocationHandlerCache中这样如果引入的是同一个服务并且这个服务在本地**
6. 如果referencedBeanName不存在对应的Bean则直接调用ReferenceBean的get()方法得到一个代理对象
##### AnnotationInjectedBeanPostProcessor代码注释
> 这个类是Spring的类
```java
public AnnotationInjectedBeanPostProcessor(Class<? extends Annotation>... annotationTypes) {
Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
this.annotationTypes = annotationTypes;
}
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
// 寻找需要注入的属性(被@Reference标注的Field也就是寻找注入点
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+ " dependencies is failed", ex);
}
return pvs;
}
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.
AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
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) {
throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
"] for annotation metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
private AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
// 哪些Filed上有@Reference注解
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
// 哪些方法上有@Reference注解
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
// 返回的是Dubbo定义的AnnotatedInjectionMetadata接下来就会使用这个类去进行属性注入
return new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
/**
* {@link Annotation Annotated} {@link InjectionMetadata} implementation
*/
private class AnnotatedInjectionMetadata extends InjectionMetadata {
private final Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements;
private final Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements;
public AnnotatedInjectionMetadata(Class<?> targetClass, Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements,
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements) {
super(targetClass, combine(fieldElements, methodElements));
this.fieldElements = fieldElements;
this.methodElements = methodElements;
}
public Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> getFieldElements() {
return fieldElements;
}
public Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> getMethodElements() {
return methodElements;
}
}
/**
* {@link Annotation Annotated} {@link Method} {@link InjectionMetadata.InjectedElement}
*/
private class AnnotatedMethodElement extends InjectionMetadata.InjectedElement {
private final Method method;
private final AnnotationAttributes attributes;
private volatile Object object;
protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) {
super(method, pd);
this.method = method;
this.attributes = attributes;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// set方法对应的属性的类型
Class<?> injectedType = pd.getPropertyType();
// 从Spring容器中获取一个Bean注意这个方法内部会生成Bean而且会缓存就像Spring中的getBean一样
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(method);
// 调用set方法
method.invoke(bean, injectedObject);
}
}
/**
* {@link Annotation Annotated} {@link Field} {@link InjectionMetadata.InjectedElement}
*/
public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement {
private final Field field;
private final AnnotationAttributes attributes;
private volatile Object bean;
protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) {
super(field, null);
this.field = field;
this.attributes = attributes;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// 给bean对象进行属性赋值
Class<?> injectedType = field.getType();
// 获取对象,然后进行注入
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
// 字段赋值injectedObject就是值
field.set(bean, injectedObject);
}
}
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
// ServiceBean:org.apache.dubbo.demo.DemoService#source=private org.apache.dubbo.demo.DemoService org.apache.dubbo.demo.consumer.comp.DemoServiceComponent.demoService#attributes={parameters=[Ljava.lang.String;@42e25b0b}
// 哪个Service应用了哪个类型的服务通过什么方式引入的
// cacheKey很鸡肋属性名不一样的时候cacheKey不一样导致不能缓存 在一个Service中@Reference两次同一个服务缓存不到
String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
Object injectedObject = injectedObjectsCache.get(cacheKey);
if (injectedObject == null) {
// // 这里会调用子类的方法生成Bean这里的子类指的就是ReferenceAnnotationBeanPostProcessor
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
```
##### ReferenceAnnotationBeanPostProcessor代码注释
> 部分注释
```java
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that Consumer service {@link Reference} annotated fields
*
* @since 2.5.7
*/
public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor implements
ApplicationContextAware, ApplicationListener {
/**
* The bean name of {@link ReferenceAnnotationBeanPostProcessor}
*/
public static final String BEAN_NAME = "referenceAnnotationBeanPostProcessor";
/**
* Cache size
*/
private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32);
private final ConcurrentMap<String, ReferenceBean<?>> referenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private final ConcurrentHashMap<String, ReferenceBeanInvocationHandler> localReferenceBeanInvocationHandlerCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedFieldReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedMethodReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private ApplicationContext applicationContext;
/**
* To support the legacy annotation that is @com.alibaba.dubbo.config.annotation.Reference since 2.7.3
*/
public ReferenceAnnotationBeanPostProcessor() {
////调用AnnotationInjectedBeanPostProcessor的构造
super(Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}
/**
* Gets all beans of {@link ReferenceBean}
*
* @return non-null read-only {@link Collection}
* @since 2.5.9
*/
public Collection<ReferenceBean<?>> getReferenceBeans() {
return referenceBeanCache.values();
}
/**
* Get {@link ReferenceBean} {@link Map} in injected field.
*
* @return non-null {@link Map}
* @since 2.5.11
*/
public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedFieldReferenceBeanMap() {
return Collections.unmodifiableMap(injectedFieldReferenceBeanCache);
}
/**
* Get {@link ReferenceBean} {@link Map} in injected method.
*
* @return non-null {@link Map}
* @since 2.5.11
*/
public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedMethodReferenceBeanMap() {
return Collections.unmodifiableMap(injectedMethodReferenceBeanCache);
}
// 该方法得到的对象会赋值给@ReferenceBean注解的属性
//
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
// 按ServiceBean的beanName生成规则来生成referencedBeanName 规则为ServiceBean:interfaceClassName:version:group
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
/*
1.相当于根据@Reference注解的信息(包括注解括号里的参数)按照toString的逻辑生成referenceBeanName这个东西是作为缓存的key
实际上是进行了一些字符串的转换啥的,不用关心
2.注意referencedBeanName和referenceBeanName的区别【一个有d字母一个没有】。referencedBeanName是ServiceBean的
名字。referenceBeanName是ReferenceBean的名字
*/
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
// 生成一个ReferenceBean对象
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
// 把referenceBean添加到Spring容器中去
registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);
//缓存注入点,不重要
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 创建一个代理对象Service中的属性被注入的就是这个代理对象
// 内部会调用referenceBean.get();
return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
}
/**
* Register an instance of {@link ReferenceBean} as a Spring Bean
*
* @param referencedBeanName The name of bean that annotated Dubbo's {@link Service @Service} in the Spring {@link ApplicationContext}
* @param referenceBean the instance of {@link ReferenceBean} is about to register into the Spring {@link ApplicationContext}
* @param attributes the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
* @param interfaceClass the {@link Class class} of Service interface
* @since 2.7.3
*/
private void registerReferenceBean(String referencedBeanName, ReferenceBean referenceBean,
AnnotationAttributes attributes,
Class<?> interfaceClass) {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 就是referenceBeanName
String beanName = getReferenceBeanName(attributes, interfaceClass);
// 当前Spring容器中是否存在referencedBeanName
if (existsServiceBean(referencedBeanName)) { // If @Service bean is local one
/**
* Get the @Service's BeanDefinition from {@link BeanFactory}
* Refer to {@link ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition}
*/
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition(referencedBeanName);
RuntimeBeanReference runtimeBeanReference = (RuntimeBeanReference) beanDefinition.getPropertyValues().get("ref"); // ServiceBean --- ref
// The name of bean annotated @Service
String serviceBeanName = runtimeBeanReference.getBeanName(); // DemoServiceImpl对应的beanName
// register Alias rather than a new bean name, in order to reduce duplicated beans
// DemoServiceImpl多了一个别名
beanFactory.registerAlias(serviceBeanName, beanName);
} else { // Remote @Service Bean
if (!beanFactory.containsBean(beanName)) {
/*
1.放入Spring容器里的应该是DemoService的实现类的代理对象但是这里放的却是referenceBean类型对象
2.referenceBean的父类有个get()可以直接获取到代理对象,那这里为啥不放呢?其实是为了支持@Autowire注解
3.首先ReferenceBean实现了Spring的FactoryBean接口,FactoryBean获取对象时是这样的调用顺序
FactoryBean.getObject() ==> getObject().get() ==>最终调子类ReferenceBean#get()
刚好给@Autowire注解注入了代理对象
*/
beanFactory.registerSingleton(beanName, referenceBean);
}
}
}
/**
* Get the bean name of {@link ReferenceBean} if {@link Reference#id() id attribute} is present,
* or {@link #generateReferenceBeanName(AnnotationAttributes, Class) generate}.
*
* @param attributes the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
* @param interfaceClass the {@link Class class} of Service interface
* @return non-null
* @since 2.7.3
*/
private String getReferenceBeanName(AnnotationAttributes attributes, Class<?> interfaceClass) {
// id attribute appears since 2.7.3
String beanName = getAttribute(attributes, "id");
// beanName为null时会进入if判断
if (!hasText(beanName)) {
beanName = generateReferenceBeanName(attributes, interfaceClass);
}
return beanName;
}
/**
* Build the bean name of {@link ReferenceBean}
*
* @param attributes the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
* @param interfaceClass the {@link Class class} of Service interface
* @return
* @since 2.7.3
*/
private String generateReferenceBeanName(AnnotationAttributes attributes, Class<?> interfaceClass) {
StringBuilder beanNameBuilder = new StringBuilder("@Reference");
if (!attributes.isEmpty()) {
beanNameBuilder.append('(');
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
beanNameBuilder.append(entry.getKey())
.append('=')
.append(entry.getValue())
.append(',');
}
// replace the latest "," to be ")"
beanNameBuilder.setCharAt(beanNameBuilder.lastIndexOf(","), ')');
}
beanNameBuilder.append(" ").append(interfaceClass.getName());
return beanNameBuilder.toString();
}
private boolean existsServiceBean(String referencedBeanName) {
return applicationContext.containsBean(referencedBeanName);
}
/**
* Get or Create a proxy of {@link ReferenceBean} for the specified the type of Dubbo service interface
*
* @param referencedBeanName The name of bean that annotated Dubbo's {@link Service @Service} in the Spring {@link ApplicationContext}
* @param referenceBeanName the bean name of {@link ReferenceBean}
* @param referenceBean the instance of {@link ReferenceBean}
* @param serviceInterfaceType the type of Dubbo service interface
* @return non-null
* @since 2.7.4
*/
private Object getOrCreateProxy(String referencedBeanName, String referenceBeanName, ReferenceBean referenceBean, Class<?> serviceInterfaceType) {
/*
1.引入的服务在Spirng容器里有说明是本项目的bean按理来说应该直接把DemoService的实现类赋给@Reference注解标注的属性
2.但实际上不是,@Reference实际上最后赋值的是一个代理对象因为除开需要执行DemoService的实现类里的方法@Reference
注解标注的属性还需要实现其它很多逻辑实现这些增强逻辑之后最终才执行的DemoService的实现类里的方法。如果直接赋值
DemoServiceImpl那么Dubbo里面的很多逻辑就走不到了
*/
if (existsServiceBean(referencedBeanName)) { // If the local @Service Bean exists, build a proxy of ReferenceBean
//wrapInvocationHandler最后也会调用referenceBean.get()其实和下面的else一样
return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
wrapInvocationHandler(referenceBeanName, referenceBean));
} else { // ReferenceBean should be initialized and get immediately
// 重点,最终从这里赋值给@Reference注解标注的属性
return referenceBean.get();
}
}
/**
* Wrap an instance of {@link InvocationHandler} that is used to get the proxy of {@link ReferenceBean} after
* the specified local referenced bean that annotated {@link @Service} exported.
*
* @param referenceBeanName the bean name of {@link ReferenceBean}
* @param referenceBean the instance of {@link ReferenceBean}
* @return non-null
* @since 2.7.4
*/
private InvocationHandler wrapInvocationHandler(String referenceBeanName, ReferenceBean referenceBean) {
return localReferenceBeanInvocationHandlerCache.computeIfAbsent(referenceBeanName, name ->
new ReferenceBeanInvocationHandler(referenceBean));
}
private static class ReferenceBeanInvocationHandler implements InvocationHandler {
private final ReferenceBean referenceBean;
private Object bean;
private ReferenceBeanInvocationHandler(ReferenceBean referenceBean) {
this.referenceBean = referenceBean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
try {
if (bean == null) { // If the bean is not initialized, invoke init()
// issue: https://github.com/apache/dubbo/issues/3429
init();
}
result = method.invoke(bean, args);
} catch (InvocationTargetException e) {
// re-throws the actual Exception.
throw e.getTargetException();
}
return result;
}
private void init() {
this.bean = referenceBean.get();
}
}
@Override
protected String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
Class<?> injectedType, InjectionMetadata.InjectedElement injectedElement) {
return buildReferencedBeanName(attributes, injectedType) +
"#source=" + (injectedElement.getMember()) +
"#attributes=" + AnnotationUtils.resolvePlaceholders(attributes, getEnvironment());
}
/**
* @param attributes the attributes of {@link Reference @Reference}
* @param serviceInterfaceType the type of Dubbo's service interface
* @return The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
*/
private String buildReferencedBeanName(AnnotationAttributes attributes, Class<?> serviceInterfaceType) {
ServiceBeanNameBuilder serviceBeanNameBuilder = create(attributes, serviceInterfaceType, getEnvironment());
return serviceBeanNameBuilder.build();
}
private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes,
Class<?> referencedType)
throws Exception {
/*
1.key就是@Reference注解最终生成的字符串,value却不是简单的代理对象
2.一个ServiceBean表示一个Dubbo服务ReferenceBean表示引用的哪个dubbo服务
3.所以ReferenceBean不是简单的代理对象他保存了诸如超时时间,轮询参数等等这些东西
【和ServcieBean很类似,ServcieBean也保存了这些东西】
4.ReferenceBean的父类ReferenceConfig#get()最终返回代理对象
5.ServiceBean和ReferenceBean思想基本上是一样的
6.ReferenceBean最终会放入缓存赋值给@Reference注解所标注的属性的依然是代理对象
*/
ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);
if (referenceBean == null) {
// 生成了一个ReferenceBean对象attributes是@Reference注解的参数值
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(attributes, applicationContext)
.interfaceClass(referencedType);
referenceBean = beanBuilder.build();
referenceBeanCache.put(referenceBeanName, referenceBean);
} else if (!referencedType.isAssignableFrom(referenceBean.getInterfaceClass())) {
throw new IllegalArgumentException("reference bean name " + referenceBeanName + " has been duplicated, but interfaceClass " +
referenceBean.getInterfaceClass().getName() + " cannot be assigned to " + referencedType.getName());
}
return referenceBean;
}
private void cacheInjectedReferenceBean(ReferenceBean referenceBean,
InjectionMetadata.InjectedElement injectedElement) {
if (injectedElement.getMember() instanceof Field) {
injectedFieldReferenceBeanCache.put(injectedElement, referenceBean);
} else if (injectedElement.getMember() instanceof Method) {
injectedMethodReferenceBeanCache.put(injectedElement, referenceBean);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ServiceBeanExportedEvent) {
onServiceBeanExportEvent((ServiceBeanExportedEvent) event);
} else if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
}
}
private void onServiceBeanExportEvent(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();
initReferenceBeanInvocationHandler(serviceBean);
}
private void initReferenceBeanInvocationHandler(ServiceBean serviceBean) {
String serviceBeanName = serviceBean.getBeanName();
// Remove ServiceBean when it's exported
ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.remove(serviceBeanName);
// Initialize
if (handler != null) {
handler.init();
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
}
@Override
public void destroy() throws Exception {
super.destroy();
this.referenceBeanCache.clear();
this.localReferenceBeanInvocationHandlerCache.clear();
this.injectedFieldReferenceBeanCache.clear();
this.injectedMethodReferenceBeanCache.clear();
}
}
```