Dubbo源码分析-Spring与Dubbo整合原理与源码分析(
Spring与Dubbo整合的整体流程(基于apache-dubbo-2.7.15)
因为dubbo有较多的兼容以前的代码比如@DubboReference 以前就有两个版本@Reference 和@com.alibaba.dubbo.config.annotation.Reference ,为了方便文章如果提到@DubboReference其实可能还包括的其他两个注解,其他情况也类似。
Spring与Dubbo整合的整体流程
- Spring启动时先根据properties配置生成9个Config对象,如应用配置,注册中心配置等。
- Spirng 与Dubbo整合主要是理解dubbo是如何利用spring提供的扩展点来处理
@DubboService和@DubboReference - 扫描
@DubboService是会生成两个spring bean 。ServiceBean和服务提供者的实际逻辑处理类(如DemoServiceImpl)。ServiceBean通过变量ref进行引用。
spring整合dubbo启动和配置
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 {
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
return registryConfig;
}
}
}
应⽤配置类为ProviderConfiguration, 在配置上有两个⽐较重要的注解
-
@PropertySource表示将dubbo-provider.properties中的配置项添加到Spring容器中,可以通过
@Value的⽅式获取到配置项中的值。 -
@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")表示对指定包下的类
进⾏扫描,扫描@DubboService与@DubboReference注解,并且进⾏处理。
在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解
1.@EnableDubboConfig
2.@DubboComponentScan
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
boolean multiple() default true;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
注意两个注解中对应的@Import注解所导⼊的类:
DubboConfigConfigurationRegistrarDubboComponentScanRegistrar
Spring在启动时会解析这两个注解,并且执⾏对应的Registrar类中的registerBeanDefinitions⽅法(这
是Spring中提供的扩展功能。)
EnableDubboConfig
Single和Multiple
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar, ApplicationContextAware {
private ConfigurableApplicationContext applicationContext;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
boolean multiple = attributes.getBoolean("multiple");
// Single Config Bindings
registerBeans(registry, DubboConfigConfiguration.Single.class);
if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
registerBeans(registry, DubboConfigConfiguration.Multiple.class);
}
// Since 2.7.6
registerCommonBeans(registry);
}
....省略
}
registerBeanDefinitions方法调用registerBeans向spring容器注册了DubboConfigConfiguration.Single.class和DubboConfigConfiguration.Multiple.class两个BeanDefinition。
public class DubboConfigConfiguration {
/**
* Single Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableConfigurationBeanBindings({
@EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class),
@EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class)
})
public static class Single {
}
/**
* Multiple Dubbo {@link AbstractConfig Config} Bean Binding
*/
@EnableConfigurationBeanBindings({
@EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
@EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
})
public static class Multiple {
}
}
从上面代码可以看出,这两个bean的作用就是为了解析dubbo的config类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigurationBeanBindingsRegister.class)
public @interface EnableConfigurationBeanBindings {
EnableConfigurationBeanBinding[] value();
}
而解析的工作由EnableConfigurationBeanBindings注解上通过Import导入的ConfigurationBeanBindingsRegister实现。
所有config的解析流程都一样,获取⽤户所设置的properties⽂件中的内容,对Properties⽂件进⾏解析,根据Properties⽂件的每个配置项的前缀、参数名、参数值⽣成。对应的BeanDefinition。
DubboConfigConfiguration.Single.class解析的是单个配置。
dubbo.application.name=dubbo-demo-annotation-provider
dubbo.application.logger=log4j
前缀为"dubbo.protocol"的配置项,会⽣成⼀个Application类型的BeanDefinition,并且
name和logger属性为对应的值。
DubboConfigConfiguration.Multiple.class解析的是复数配置。
dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20880
dubbo.protocols.p1.host=127.0.0.1
dubbo.protocols.p2.name=dubbo
dubbo.protocols.p2.port=20881
dubbo.protocols.p2.host=127.0.0.1
⽐如前缀为"dubbo.protocols"的配置项,会⽣成两个ProtocolConfig类型的BeanDefinition,两个
BeanDefinition的beanName分别为p1和p2。
ConfigurationBeanBindingsRegister
public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private ConfigurableEnvironment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取配置11的属性值
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName()));
AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar();
registrar.setEnvironment(environment);
//循环注册
for (AnnotationAttributes element : annotationAttributes) {
registrar.registerConfigurationBeanDefinitions(element, registry);
}
}
....省略
registerBeanDefinitions 中首先获取@EnableConfigurationBeanBindings中的配置的11个@EnableConfigurationBeanBinding属性值,然后循环调用registrar.registerConfigurationBeanDefinitions(element, registry)方法进行解析注册。
registerConfigurationBeanDefinitions逻辑比较简单,我们直接看它调用的方法->registerConfigurationBeans
private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple,
boolean ignoreUnknownFields, boolean ignoreInvalidFields,
BeanDefinitionRegistry registry) {
//①解析 prefix前缀的所有配置
Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(environment.getPropertySources(), environment, prefix);
...省略无关代码
// Single和Multiple 他们调用的都是同一段代码,只有些许不一样,比如bean的名字。
Set<String> beanNames = multiple ? resolveMultipleBeanNames(configurationProperties) :
singleton(resolveSingleBeanName(configurationProperties, configClass, registry));
for (String beanName : beanNames) {
//②注册config
registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields,
configurationProperties, registry);
}
//③注册一个beanPostProcessor用来给config赋值。
registerConfigurationBindingBeanPostProcessor(registry);
}
registerConfigurationBeans主要做一下事情。
- 获取 prefix前缀的所有配置
- 循环调用
registerConfigurationBean注册config - 注册一个
beanPostProcessor用来给config赋值。(ConfigurationBeanBindingPostProcessor)
registerConfigurationBean
private void registerConfigurationBean(String beanName, Class<?> configClass, boolean multiple,
boolean ignoreUnknownFields, boolean ignoreInvalidFields,
Map<String, Object> configurationProperties,
BeanDefinitionRegistry registry) {
//生成一个BeanDefinitionBuilder
BeanDefinitionBuilder builder = rootBeanDefinition(configClass);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
setSource(beanDefinition);
// 解析参数,因为properties可能出现使用占位符的情况。
Map<String, Object> subProperties = resolveSubProperties(multiple, beanName, configurationProperties);
//设置 beanDefinition属性
initBeanMetadataAttributes(beanDefinition, subProperties, ignoreUnknownFields, ignoreInvalidFields);
//注册beanDefinition
registry.registerBeanDefinition(beanName, beanDefinition);
registerConfigurationBean 逻辑比较简单,就是生成 一个beanDefinition并赋值属性,然后向sping注册。
initBeanMetadataAttributes方法注册的属性会在上述第三步注册的beanPostProcessor用来给config赋值。
static void initBeanMetadataAttributes(AbstractBeanDefinition beanDefinition,
Map<String, Object> configurationProperties,
boolean ignoreUnknownFields, boolean ignoreInvalidFields) {
//放置属性
beanDefinition.setAttribute(CONFIGURATION_PROPERTIES_ATTRIBUTE_NAME, configurationProperties);
beanDefinition.setAttribute(IGNORE_UNKNOWN_FIELDS_ATTRIBUTE_NAME, ignoreUnknownFields);
beanDefinition.setAttribute(IGNORE_INVALID_FIELDS_ATTRIBUTE_NAME, ignoreInvalidFields);
}
registerConfigurationBindingBeanPostProcessor
其实就是向spring容器注册一个ConfigurationBeanBindingPostProcessor,在spring执行的时候会回调
BeanPostProcessor的方法
registerInfrastructureBean(registry, ConfigurationBeanBindingPostProcessor.BEAN_NAME,
ConfigurationBeanBindingPostProcessor.class);
ConfigurationBeanBindingPostProcessor我们主要看postProcessBeforeInitialization方法,这就是spring bean生命周期的初始化前方法。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
BeanDefinition beanDefinition = getNullableBeanDefinition(beanName);
//判断是否是配置类
if (isConfigurationBean(bean, beanDefinition)) {
//给config类中的属性配置值
bindConfigurationBean(bean, beanDefinition);
//这个方法唯一实现类已经被废弃,后期可能会删除
customize(beanName, bean);
}
return bean;
}
当某个AbstractConfig类型的Bean,在经过DubboConfigBindingBeanPostProcessor处理时,此时
Bean对象中的属性是没有值的,会利⽤DefaultDubboConfigBinder进⾏赋值。底层就是利⽤Spring中的
DataBinder技术,结合properties⽂件对对应的属性进⾏赋值。
对应⼀个AbstractConfig类型(针对的其实是⼦类,⽐如ApplicationConfig、RegistryConfig)的
Bean,每个类都有⼀些属性,⽽properties⽂件是⼀个key-value对,所以实际上DataBinder就是将属性
名和properties⽂件中的key进⾏匹配,如果匹配成功,则把value赋值给属性。
举个例子
dubbo.application.name=dubbo-demo-annotation-provider
dubbo.application.logger=log4j
对于此配置,它对应ApplicationConfig对象(beanName是⾃动⽣成的),所以最终ApplicationConfig
对象的name属性的值为“dubbo-demo-provider1-application”,logger属性的值为“log4j”。
生成config的流程分析完了,其实逻辑还是比较简单,根据前缀获取属性值->生成beanDefinition ->再bean的生命周期”初始化”前进行属性赋值。
registerCommonBeans
回到DubboConfigConfigurationRegistrar类的registerBeanDefinitions方法,除了注册 DubboConfigConfiguration.Single.class和DubboConfigConfiguration.Multiple.class生成config。在方法的最后调用registerCommonBeans向spring容器注册了几个BeanPostProcessor。
public static void registerCommonBeans(BeanDefinitionRegistry registry) {
// 这是比较重要的BeanPostProcessor,是用来解析@DubboReference注解的
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.class);
//这个类的作用是想spring注册两个监听器DubboBootstrapApplicationListener和createDubboLifecycleComponentApplicationListener
registerInfrastructureBean(registry, DubboApplicationListenerRegistrar.BEAN_NAME,
DubboApplicationListenerRegistrar.class);
// Since 2.7.6 https://github.com/apache/dubbo/issues/5721
registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
DubboConfigDefaultPropertyValueBeanPostProcessor.class);
// 这个方法在dubbo3.0版本会被废弃。不影响后面解读流程。
registerInfrastructureBean(registry, DubboConfigEarlyRegistrationPostProcessor.BEAN_NAME,
DubboConfigEarlyRegistrationPostProcessor.class);
}
这个方法最重要的是注册ReferenceAnnotationBeanPostProcessor和DubboApplicationListenerRegistrar其他都是为了解决GitHub提出的问题。
在DubboApplicationListenerRegistrar类中想spring注册了两个监听器DubboBootstrapApplicationListener和DubboLifecycleComponentApplicationListener。
-
DubboBootstrapApplicationListener的作用是监听spring启动和销毁事件,用于暴露和销毁dubbo服务 -
DubboLifecycleComponentApplicationListener也是监听spring启动和销毁事件,只是它用于执行dubbo生命周期的方法。
@DubboComponentScan
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceClassPostProcessor(packagesToScan, registry);
// @since 2.7.6 Register the common beans 上面已经分析过了
//这里为什么会再调用一遍,因为spring的bean注册比较灵活,不通过DubboComponentScan也可以实现扫描的。不必纠结。
registerCommonBeans(registry);
}
主要逻辑
- 解析配置的扫描路径
- 调用
registerServiceClassPostProcessor方法会向spring中注册一个用于解析注解@DubboService的ServiceClassPostProcessor他是一个BeanFactoryPostProcessor(注意不是BeanPostProcessor,这里的命名有点问题。BeanFactoryPostProcessor和BeanPostProcessor执行时机是不一样的,spring,mybatis也是通过实现BeanFactoryPostProcessor扫描并向spring注册对象的)。 - 调用
registerCommonBeans注册的bean,上面已经分析过了这里为什么会再调用一遍,因为spring的bean注册比较灵活,不通过DubboComponentScan也可以实现扫描的。不必纠结。
ServiceClassPostProcessor
为了兼容以前的版本,这里扫描了三个注解。
private static final List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
DubboService.class,
Service.class,
com.alibaba.dubbo.config.annotation.Service.class
);
在spring启动过程中会自动调用postProcessBeanDefinitionRegistry方法。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// @since 2.7.5
registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
// 解析占位符
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
//扫描DubboService , Service, com.alibaba.dubbo.config.annotation.Service
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
postProcessBeanDefinitionRegistry这个方法并没有做太多事情,主要逻辑在registerServiceBeans方法
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 用于扫描我们的包路径和对于注解
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
// bean 名称生成器
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
//扫描DubboService , Service, com.alibaba.dubbo.config.annotation.Service
serviceAnnotationTypes.forEach(annotationType -> {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
});
for (String packageToScan : packagesToScan) {
// 设置扫描的路径
scanner.scan(packageToScan);
// 一、 扫描生成BeanDefinition,并向spring注册最后返回BeanDefinitionHolder(注意向这里扫描并注册的是DemoServiceImpl)
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 二、注册 ServiceBean
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
.......省略部分代码
}
}
}
}
在文章开头的流程图上,所说的扫描DubboService 会生成两个bean(ServiceBean和DemoServiceImpl)就是在注释的的第一和第二步。
findServiceBeanDefinitionHolders逻辑比较简单,扫描生成BeanDefinition,并向spring注册最后返回BeanDefinitionHolder,扫描逻辑都是spring提供的。
我们重点分析registerServiceBean方法。
registerServiceBean
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
// 一,拿到服务提供者类DemoServiceImpl
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// 二、拿到@DubboService注解
Annotation service = findServiceAnnotation(beanClass);
// 三、拿到@DubboService注解配置属性
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// 四、拿到接口DemoService
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
//五、拿到DemoServiceImpl bean名字
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
//六、构建 ServiceBean的BeanDefinition
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// 七、生成ServiceBean 的名字
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) {
// 八、注册ServiceBean
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
if (!serviceBeanDefinition.getPropertyValues().contains("id")) {
serviceBeanDefinition.getPropertyValues().addPropertyValue("id", beanName);
}
...........省略代码
}
...........省略代码
}
}
registerServiceBean处理逻辑如下。
- 拿到服务提供者类DemoServiceImpl
- 获取
@DubboService注解 - 获取
@DubboService注解配置属性 - 获取接口DemoService
- 获取DemoServiceImpl bean名字
- 构建
ServiceBean的BeanDefinition - 生成ServiceBean 的名字
- 注册ServiceBean。
我们稍微分析第6个步骤。构建 ServiceBean的BeanDefinition的方法buildServiceBeanDefinition
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
//这里省略的属性下面都有单独处理,为什么要省略,因为我们在注解上配置是名字,但是在ServiceBean中,这些都是对象。
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
// 给ref赋值annotatedServiceBeanName是DemoServiceImpl 的bean名字。
addPropertyReference(builder, "ref", annotatedServiceBeanName);
// 下面逻辑大同小异,不在赘述。就是给SeriviceBean属性赋值。
builder.addPropertyValue("interface", interfaceClass.getName());
builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
if (StringUtils.hasText(providerConfigBeanName)) {
addPropertyReference(builder, "provider", providerConfigBeanName);
}
String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
if (StringUtils.hasText(monitorConfigBeanName)) {
addPropertyReference(builder, "monitor", monitorConfigBeanName);
}
String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
if (StringUtils.hasText(applicationConfigBeanName)) {
addPropertyReference(builder, "application", applicationConfigBeanName);
}
String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
if (StringUtils.hasText(moduleConfigBeanName)) {
addPropertyReference(builder, "module", moduleConfigBeanName);
}
String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");
List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
if (!registryRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("registries", registryRuntimeBeanReferences);
}
String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");
List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
if (!protocolRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
}
return builder.getBeanDefinition();
该方法逻辑比较简单,开始之所以忽略部分属性,因为这些属性在ServiceBean中是对象,需要单独转换赋值。另外我们注意到ref赋值就是在这里。
到这里关于@DubboService的扫描,解析,注册就已经分析完了。(todo 补充流程图)
ReferenceAnnotationBeanPostProcessor
最后我们来分析 @DubboReference的处理流程。@DubboReference的处理由ReferenceAnnotationBeanPostProcessor后置处理器来处理。
对比@DubboReference,会联想到@Autowired注解,其实他们的实现原理都类似。
@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
@DubboReference
private DemoService demoService;
@Autowired
private DemoService demoService;
@Override
public String sayHello(String name) {
return demoService.sayHello(name);
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return null;
}
}
spring启动过程中先会执行ReferenceAnnotationBeanPostProcessor的父类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 (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+ " dependencies is failed", ex);
}
return pvs;
}
postProcessPropertyValues 方法主要有两步
- 寻找注入点,注入点包括方法或者字段上是否有@DubboReference
- 调用
metadata.inject进行属性注入
寻找注入点
findInjectionMetadata方法逻辑比较简单,主要是缓存的判断。直接看它调用的buildAnnotatedMetadata方法。
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
// 1. 寻找字段上的注入点
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
// 2. 寻找方法上的注入点
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
//AnnotatedInjectionMetadata可以理解为注入点的集合。
return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
buildAnnotatedMetadata方法分成三部
- 寻找字段上的注入点
- 寻找方法上的注入点
- 把注入点合并为
AnnotatedInjectionMetadata。AnnotatedInjectionMetadata是字段和方法注入点的抽象。
寻找字段上的注入点
private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {
final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement>();
// 遍历类中的所有字段
ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// 判断字段上是否有DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class 注解
for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
AnnotationAttributes attributes = doGetAnnotationAttributes(field, annotationType);
if (attributes != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field);
}
return;
}
// 如果有则创建一个AnnotatedFieldElement 放到集合中。
elements.add(new AnnotatedFieldElement(field, attributes));
}
}
}
});
return elements;
}
寻找字段上的注入点,就是遍历类(包括父类)中的所有字段判断是否有@DubboReference注解。
如果有就新建一个AnnotatedFieldElement对象放到集合中。寻找方法上的注入点也类似,不过方法的注入点使用的是AnnotatedMethodElement对象。
属性注入
我们以字段注入讲解。进入AnnotatedFieldElement的inject方法。
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// 1. 解析注入点类型
Class<?> injectedType = resolveInjectedType(bean, field);
// 2. 获取注入对象
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
// 3. 字段注入
field.set(bean, injectedObject);
}
inject 逻辑分为三步:
- 解析注入点类型
- 获取注入对象
- 字段注入
getInjectedObject获取注入对象的逻辑如下
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
Object injectedObject = injectedObjectsCache.get(cacheKey);
if (injectedObject == null) {
// 缓存中没有调用doGetInjectedBean
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
先判断缓存中有没有,没有则调用doGetInjectedBean方法。
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
// 1. ServiceBean 的名字ServiceBean:org.apache.dubbo.demo.DemoService
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
// 2. ReferenceBean 的名字@Reference org.apache.dubbo.demo.DemoService
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
referencedBeanNameIdx.computeIfAbsent(referencedBeanName, k -> new TreeSet<String>()).add(referenceBeanName);
// 3. 生成ReferenceBean 对象,并赋值
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
// 4. 判断本地spring容器中是否有对应的ServiceBean对象
boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);
// 5. 如果本地有ServiceBean对象 并且没有暴露,则提前暴露服务
prepareReferenceBean(referencedBeanName, referenceBean, localServiceBean);
// 6. 如果是本地调用会注入一个别名,否则向spring 容器中注入ReferenceBean
registerReferenceBean(referencedBeanName, referenceBean, localServiceBean, referenceBeanName);
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 7. 调用referenceBean.get()方法,生成代理对象。
return getBeanFactory().applyBeanPostProcessorsAfterInitialization(referenceBean.get(), referenceBeanName);
}
这是 @DubboReference属性注入的核心逻辑。
- 生成ServiceBean 的名字
ServiceBean:org.apache.dubbo.demo.DemoService,用于区分是本地是否有对应的dubbo服务 - 生成
ReferenceBean的名字@Reference org.apache.dubbo.demo.DemoService - 生成
ReferenceBean对象,并赋值 - 判断本地spring容器中是否有对应的
ServiceBean对象 - 如果本地有
ServiceBean对象 并且没有暴露,则提前暴露服务 - 如果是本地调用会注入一个别名,否则向spring 容器中注入
ReferenceBean - 调用
referenceBean.get()方法,生成代理对象。生成完代理对象会继续执行spring初始化后方法,进行AOP等操作。
至此,spring整合dubbo的代码分析完了。