Spring与Dubbo整合的整体流程(基于apache-dubbo-2.7.15)
因为dubbo有较多的兼容以前的代码比如@DubboReference 以前就有两个版本@Reference 和@com.alibaba.dubbo.config.annotation.Reference ,为了方便文章如果提到@DubboReference其实可能还包括的其他两个注解,其他情况也类似。
- 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
注解所导⼊的类:
DubboConfigConfigurationRegistrar
DubboComponentScanRegistrar
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提出的问题。
在DubboApplicationListenerRegistra
r类中想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也是通过实现BeanFactoryPostProcesso
r扫描并向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的代码分析完了。