10. Dubbo与Spring整合 - @DubboReference
本文主要介绍Dubbo
里@DubboReference
注解的解析过程。
1. 使用示例
先看一个@DubboReference
的简单用法,示例很简单,在一个类中在要注入Dubbo服务的属性加上@DubboReference
注解即可。
@Service
public class HelloService {
@DubboReference
private DemoService demoService;
@Override
public String sayHello(String name) {
return demoService.sayHello(name);
}
}
2. 源码分析
@DubboReference
解析入口是在DubboComponentScanRegistrar
类的registerBeanDefinitions
方法中,关于这个类以及方法的介绍上一篇文章已经介绍了,这里不再赘述。registerBeanDefinitions
方法中调用registerCommonBeans
方法就是为了处理@DubboReference
注解的。
2.1 注册PostProcessor
public static void registerCommonBeans(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.class);
registerInfrastructureBean(registry, DubboApplicationListenerRegistrar.BEAN_NAME,
DubboApplicationListenerRegistrar.class);
registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
DubboConfigDefaultPropertyValueBeanPostProcessor.class);
registerInfrastructureBean(registry, DubboConfigEarlyRegistrationPostProcessor.BEAN_NAME,
DubboConfigEarlyRegistrationPostProcessor.class);
}
该方法向Spring容器里注册了几个Bean。
ReferenceAnnotationBeanPostProcessor
用来处理@DubboReference
注解DubboConfigAliasPostProcessor
用来处理别名。DubboApplicationListenerRegistrar
用来监听Spring事件,启动dubbo服务的监听器。DubboConfigDefaultPropertyValueBeanPostProcessor
为Dubbo的部分配置属性赋值默认值。DubboConfigEarlyRegistrationPostProcessor
用来处理可以提前暴露的Dubbo配置对象。
@DubboReference
注解和@Autowired
注解功能类似,都是为注解的属性进行赋值。 我们知道@Autowired
注解是由AutowiredAnnotationBeanPostProcessor
类来处理的,那么@DubboReference
注解也是有对应的类来处理的,即ReferenceAnnotationBeanPostProcessor
。
2.2 DubboReference注解的PostProcessor
ReferenceAnnotationBeanPostProcessor
会解析3个注解,即:@DubboReference
、@org.apache.dubbo.config.annotation.Reference
和@com.alibaba.dubbo.config.annotation.Reference
。
这个类中比较重要的方法为doGetInjectedBean
,是调用这个方法来获得一个对象,并把这个对象赋值为对应的属性。
那这个方法是如何调过来的呢?这就涉及到Spring的内容了,这里简单过下。
ReferenceAnnotationBeanPostProcessor
类继承了AbstractAnnotationBeanPostProcessor
类,这个类中有个处理属性的方法为postProcessPropertyValues
。在postProcessPropertyValues
方法中有个寻找注入点的方法为findInjectionMetadata
。拿到注入点后调用inject
方法进行注入。
2.3 属性赋值
doGetInjectedBean
返回的是一个代理对象。下面来看下这个对象的生成过程。
先调用
buildReferencedBeanName
方法得到一个bean的名字,这个bean为需要引入服务的beanName,最终生成的名称格式为ServiceBean:org.apache.dubbo.demo.DemoService
,如果@Reference
注解设置group
、version
属性,也会把对应的值给拼接上去。下面代码为,构造这个beanName的过程。最终格式:
ServiceBean:interfaceClassName:version:group
调用
getReferenceBeanName
方法,得到referenceBeanName
,这个和referencedBeanName
很相似,但含义是不一样的。referencedBeanName
对应的是ServiceBean
服务的名字,而referenceBeanName
为我引入时产生的名字。产生规则为,
@Reference
注解所有属性的值进行拼接,所以就是@Reference
引入的接口是同一个,但注解的值不一样的话,这个名字也是不一样的。那么这个名字的作用是什么呢?他的作用是作为一个key来进行缓存得到的对象。如果有多处
@Reference
引入的是同一个对象,那么直接从缓存里取就好了。构造
ReferenceBean
判断是否为本地暴露的服务
如果是本地暴露的服务,直接从spring容器里获取
把referenceBean添加到Spring容器中去
对注入点进行缓存
调用其他
BeanPostProcessor
实例化后的方法
2.4 构建ReferenceBean
先根据referenceBeanName
的值,从缓存里查找有没有生成过,如果生成过直接返回,如果没有的话,生成一个ReferenceBean
并放入缓存。
ReferenceBean
和ServiceBean
是对应的,代表要引入的Dubbo服务。下面代码为构造ReferenceBean
的主要代码。
private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes,
Class<?> referencedType)
throws Exception {
ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);
if (referenceBean == null) {
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(attributes, applicationContext)
.interfaceClass(referencedType)
.beanName(referenceBeanName);
// 构造referenceBean
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;
}
public final C build() throws Exception {
checkDependencies();
// new 一个 referenceBean
C configBean = doBuild();
// 对referenceBean属性进行配置
configureBean(configBean);
if (logger.isInfoEnabled()) {
logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
}
return configBean;
}
protected void configureBean(C configBean) throws Exception {
// 将@Reference注解中的配置项的值赋值给configBean
preConfigureBean(attributes, configBean);
// 从spring容器中获取注册中心信息赋值给configBean
configureRegistryConfigs(configBean);
// 从spring容器中获取监控中心信息赋值给configBean
configureMonitorConfig(configBean);
// 从spring容器中获取应用信息赋值给configBean
configureApplicationConfig(configBean);
// module info
configureModuleConfig(configBean);
// 设置applicationContext、interfaceName、consumer、methods属性
postConfigureBean(attributes, configBean);
}
2.4 注册ReferenceBean
如果是本地服务的话,取到beanDefinition
的ref
属性,再获取到对应serviceBeanName
,将为serviceBeanName
注册一个别名与referenceBeanName
进行关联。
如果不是本地服务的话,判断beanFactory
中是否存在这个beanName
,如果不存在的话,将它注册到spring容器中,这一步是为了使用方便,让@Autowired
也能引入Dubbo服务。
2.5 与@Autowired的渊源
通过上面对@DubboReference
的解析我们可以知道,只要使用过@DubboReference
引入一个服务,Dubbo会把ReferenceBean
和beanName
放入bean工厂
中,那这样的话,我们就可以通过@Autowired
注解来引入Dubbo服务了,所以如果在代码中看到用@Autowired
来标注一个Dubbo接口而且还能使用就不足为奇了。
@Service
public class HelloService {
@DubboReference
private DemoService demoService;
@Autowired
private DemoService demoService1;
@Override
public String sayHello(String name) {
return demoService.sayHello(name);
}
}
后记
@DubboReference
解析以及处理基本上已经结束了,至于Dubbo是如何对引入的服务进行调用,这块后面再进行介绍。