参考资料:Dubbo系列之(四)服务订阅(1)
介绍
dubbo的服务订阅有两种方式,第一种是通过xml文件的标签<dubbo:reference />
,第二种是通过注解@Reference
。两者在使用上没有什么区别,标签上的属性都可以在注解上找到对应的配置。在源码实现上,两者存在一定的区别和共同点。
共同点:两者最终都是调用com.alibaba.dubbo.config.ReferenceConfig#get
方法来产生代理对象和订阅服务
区别:
- 标签<dubbo:reference />的实现方式是通过spring自定义标签实现的。dubbo使用ReferenceBean解析标签内容,ReferenceBean实现了FactoryBean接口,因此可通过getObject方法创建具体的实例对象,该方法里面就是调用父类ReferenceConfig#get方法。
+ 1
2
3
4
5
6
7
8
java复制代码public class DubboNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 省略部分代码...
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
}
}
+ 
+ 
- @Reference注解是通过后置处理器实现的,与@Autowired、@Qualifier等注解类似,都是往Spring Bean对象注入指定属性。dubbo框架自己编写类似AutowiredAnnotationBeanPostProcessor的后置处理器,叫做ReferenceAnnotationBeanPostProcessor,通过这个后置处理器来解析被@Reference注解表标注的变量和方法。
+ `com.alibaba.dubbo.config.annotation.Reference`
+ 1
2
3
4
5
6
7
8
9
10
11
12
java复制代码@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Reference {
Class<?> interfaceClass() default void.class;
String interfaceName() default "";
String url() default "";
boolean check() default true;
int retries() default 2;
int timeout() default 0;
// 省略部分信息
}
总结
主线:postProcessPropertyValues -> findInjectionMetadata -> inject
步骤:
- 利用spring提供的扩展点,InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法,该方法在spring bean对象属性赋值时触发;
- dubbo编写ReferenceAnnotationBeanPostProcessor后置处理器,实现InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法;
- 先获取目标类上被**@Reference**标注的成员变量和方法的元信息,把元信息集合封装到InjectionMetadata对象;
- 遍历被标注的成员变量和成员方法,两者都是通过反射完成属性值的注入。如果是成员变量,调用
field.set()
,如果是成员方法,调用method.invoke()
- 属性值的获取过程主要分为三步:
- 创建ReferenceBean对象,并放入缓存
- 创建ReferenceBeanInvocationHandler对象,接着执行
handler#init
方法,里面是调用ReferenceBean#get
方法,进入真正的服务订阅- 最后,通过jdk动态代理,返回一个代理对象
ReferenceAnnotationBeanPostProcessor解析
spring相关:属性依赖注入的扩展点
参考资料:
在解析远程服务引入前,我们先了解一个特殊的后置处理器,InstantiationAwareBeanPostProcessor,源码如下:
1 | java复制代码public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { |
单词 | 含义 |
---|---|
Instantiation | 表示实例化,对象还未生成 |
Initialization | 表示初始化,对象已经生成 |
dubbo就是对上面的postProcessPropertyValues方法进行扩展,给对象注入依赖时,创建代理对象,订阅远程服务。
先来看下ReferenceAnnotationBeanPostProcessor的继承关系,然后重点关注postProcessPropertyValues方法。
由于ReferenceAnnotationBeanPostProcessor没有实现postProcessPropertyValues方法,所以我们看到的是其父类AnnotationInjectedBeanPostProcessor
AnnotationInjectedBeanPostProcessor#postProcessPropertyValues
1 | java复制代码@Override |
该方法先通过findInjectionMetadata()
得到被注解标注的成员变量和方法的元信息,把这些信息集合封装成InjectionMetadata对象,最后调用inject方法,内部使用for循环将属性值逐个注入。
org.springframework.beans.factory.annotation.InjectionMetadata#inject
1 | java复制代码public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable { |
spring相关:如何找到@Reference标注的成员
通过上面小节,我们知道当某个Spring Bean对象需要引入dubbo服务时,是通过@Reference注入服务提供者的实例对象,原理是通过postProcessPropertyValues方法注入。接下来,我们需要了解怎么找到@Reference标注的成员,包括成员变量和成员方法。
AnnotationInjectedBeanPostProcessor#findInjectionMetadata
1 | java复制代码/** |
上面一大段方法,要表达的内容是:先从缓存获取属性的元信息,如果没有,调用buildAnnotatedMetadata方法获取。
AnnotationInjectedBeanPostProcessor#buildAnnotatedMetadata
1 | java复制代码private AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) { |
AnnotationInjectedBeanPostProcessor#findFieldAnnotationMetadata
1 | java复制代码/** |
AnnotationInjectedBeanPostProcessor#findAnnotatedMethodMetadata
1 | java复制代码/** |
总的来说,dubbo都是通过AnnotationUtils#findAnnotation
方法获取到被注解@Reference标注成员变量和成员方法。
如何实现依赖注入
获取到需要被@Reference标注的成员元信息后,接着就是属性值的注入。dubbo提供了两个注入类,分别是AnnotatedFieldElement和AnnotatedMethodElement,显然,一个用于处理成员变量,一个是用于处理成员方法。
AnnotatedFieldElement#inject
1 | java复制代码@Override |
AnnotatedMethodElement#inject
1 | java复制代码@Override |
获取属性值
上面两个方法都需要通过getInjectedObject()
获取注入的实例对象,该方法先从缓存获取结果,如果没有,则调用doGetInjectBean()
创建对象
ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
1 | java复制代码/** |
doGetInjectedBean方法先是构建ReferenceBean对象,然后通过jdk动态代理返回一个代理对象。
在构建代理对象前,需要先创建一个ReferenceBeanInvocationHandler
,接着handler会调用ReferenceBean#get
方法,这个方法是服务订阅的核心方法,下篇文章会讲解。得到InvocationHandler对象后,就通过jdk动态代理返回代理对象。
源码如下:
1 | java复制代码// ReferenceAnnotationBeanPostProcessor#buildProxy |
ReferenceBeanInvocationHandler
源码如下
1 | java复制代码private static class ReferenceBeanInvocationHandler implements InvocationHandler { |
本文转载自: 掘金