dubbo服务订阅_Reference注解

参考资料: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));
}
}
+ ![image-20201221145847852](https://gitee.com/songjianzaina/juejin_p12/raw/master/img/b78aa47400d40716d2ee760ef4a13495426a1e41d59388aea1152df6bc13b222) + ![image-20201221145924255](https://gitee.com/songjianzaina/juejin_p12/raw/master/img/a5165efa7da40530039163cb4c796f8467a6c51b618f5a29131be0bc3cdd801f)
  • @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

步骤:

  1. 利用spring提供的扩展点,InstantiationAwareBeanPostProcessorpostProcessPropertyValues方法,该方法在spring bean对象属性赋值时触发;
  2. dubbo编写ReferenceAnnotationBeanPostProcessor后置处理器,实现InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法;
  3. 先获取目标类上被**@Reference**标注的成员变量和方法的元信息,把元信息集合封装到InjectionMetadata对象;
  4. 遍历被标注的成员变量和成员方法,两者都是通过反射完成属性值的注入。如果是成员变量,调用field.set(),如果是成员方法,调用method.invoke()
  5. 属性值的获取过程主要分为三步:
    1. 创建ReferenceBean对象,并放入缓存
    2. 创建ReferenceBeanInvocationHandler对象,接着执行handler#init方法,里面是调用ReferenceBean#get方法,进入真正的服务订阅
    3. 最后,通过jdk动态代理,返回一个代理对象

ReferenceAnnotationBeanPostProcessor解析

spring相关:属性依赖注入的扩展点

参考资料:

在解析远程服务引入前,我们先了解一个特殊的后置处理器,InstantiationAwareBeanPostProcessor,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

/**
* 在bean实例化前执行的回调方法,先于postProcessBeforeInitialization执行
*/
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;

/**
* 在bean实例化后,属性显式填充和自动注入前回调
*/
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;

/**
* 关键!
* 给属性赋值
*/
PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;

}
单词 含义
Instantiation 表示实例化,对象还未生成
Initialization 表示初始化,对象已经生成

dubbo就是对上面的postProcessPropertyValues方法进行扩展,给对象注入依赖时,创建代理对象,订阅远程服务。

先来看下ReferenceAnnotationBeanPostProcessor的继承关系,然后重点关注postProcessPropertyValues方法。

image-20201221153451683

由于ReferenceAnnotationBeanPostProcessor没有实现postProcessPropertyValues方法,所以我们看到的是其父类AnnotationInjectedBeanPostProcessor

AnnotationInjectedBeanPostProcessor#postProcessPropertyValues

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

// 查找当前Bean对象需要注入的成员的元信息,包括成员变量和方法
// 即被@Reference标注的成员,把这些元信息集合封转成一个InjectionMetadata对象
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
// 注入属性值
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException("xxx");
}
return pvs;
}

该方法先通过findInjectionMetadata()得到被注解标注的成员变量和方法的元信息,把这些信息集合封装成InjectionMetadata对象,最后调用inject方法,内部使用for循环将属性值逐个注入。

org.springframework.beans.factory.annotation.InjectionMetadata#inject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
Collection<InjectedElement> elementsToIterate =
(this.checkedElements != null ? this.checkedElements : this.injectedElements);

if (!elementsToIterate.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : elementsToIterate) {
if (debug) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
// 调用具体实现类的inject方法,接下来会分析这块
element.inject(target, beanName, pvs);
}
}
}

spring相关:如何找到@Reference标注的成员

通过上面小节,我们知道当某个Spring Bean对象需要引入dubbo服务时,是通过@Reference注入服务提供者的实例对象,原理是通过postProcessPropertyValues方法注入。接下来,我们需要了解怎么找到@Reference标注的成员,包括成员变量和成员方法。

AnnotationInjectedBeanPostProcessor#findInjectionMetadata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
java复制代码/**
* 找到需要注入的成员元信息,封装成InjectionMetadata
*
* @param beanName 当前需要被注入的对象名
* @param clazz 当前需要被注入的类对象
* @param pvs 当前需要被注入对象的参数
* @return 返回需要注入的成员元信息
*/
public InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// 获取缓存key,默认是beanName,否则是claaName
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());

// 先从缓存获取,获取不到,或者注入属性的元数据所在的目标类与当前被注入的类不一致,需要重新获取
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;
}

上面一大段方法,要表达的内容是:先从缓存获取属性的元信息,如果没有,调用buildAnnotatedMetadata方法获取。

AnnotationInjectedBeanPostProcessor#buildAnnotatedMetadata

1
2
3
4
5
6
7
8
9
10
java复制代码private AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
// 查找被标注的成员变量的元信息
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements =
findFieldAnnotationMetadata(beanClass);
// 查找被标注的成员方法的元信息
Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements =
findAnnotatedMethodMetadata(beanClass);

return new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}

AnnotationInjectedBeanPostProcessor#findFieldAnnotationMetadata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
java复制代码/**
* Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link A} fields
*
* @param beanClass The {@link Class} of Bean
* @return non-null {@link List}
*/
private List<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {

final List<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> elements =
new LinkedList<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement>();

ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// 遍历每个成员变量,并通过getAnnotationType()指定注解类型(@Reference),最后得到结果后存入集合
A annotation = getAnnotation(field, getAnnotationType());
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + getAnnotationType().getName() + " is not supported on static fields: " + field);
}
return;
}
elements.add(new AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement(field, annotation));
}
}
});
return elements;
}

AnnotationInjectedBeanPostProcessor#findAnnotatedMethodMetadata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
java复制代码/**
* Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link A @A} methods
*
* @param beanClass The {@link Class} of Bean
* @return non-null {@link List}
*/
private List<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {

final List<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> elements =
new LinkedList<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement>();

ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = findBridgedMethod(method);
if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// 遍历每个方法,获取被@Reference标注的方法
A annotation = findAnnotation(bridgedMethod, getAnnotationType());
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
// 省略部分异常检查
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
elements.add(new AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement(method, pd, annotation));
}
}
});
return elements;
}

总的来说,dubbo都是通过AnnotationUtils#findAnnotation方法获取到被注解@Reference标注成员变量和成员方法。

如何实现依赖注入

获取到需要被@Reference标注的成员元信息后,接着就是属性值的注入。dubbo提供了两个注入类,分别是AnnotatedFieldElement和AnnotatedMethodElement,显然,一个用于处理成员变量,一个是用于处理成员方法。

AnnotatedFieldElement#inject

1
2
3
4
5
6
7
8
9
10
11
java复制代码@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// 获取属性的类对象
Class<?> injectedType = field.getType();
// 获取注入的实例对象
injectedBean = getInjectedObject(annotation, bean, beanName, injectedType, this);
// 反射,允许访问
ReflectionUtils.makeAccessible(field);
// 属性赋值
field.set(bean, injectedBean);
}

AnnotatedMethodElement#inject

1
2
3
4
5
6
7
8
9
10
11
java复制代码@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// 获取参数的类对象
Class<?> injectedType = pd.getPropertyType();

injectedBean = getInjectedObject(annotation, bean, beanName, injectedType, this);

ReflectionUtils.makeAccessible(method);
// 调用方法
method.invoke(bean, injectedBean);
}

获取属性值

上面两个方法都需要通过getInjectedObject()获取注入的实例对象,该方法先从缓存获取结果,如果没有,则调用doGetInjectBean()创建对象

ReferenceAnnotationBeanPostProcessor#doGetInjectedBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码/**
* 该方法是一个模板方法,用来得到一个指定注入类型的对象
*
* @param reference
* @param bean Current bean that will be injected
* @param beanName Current bean name that will be injected
* @param injectedType the type of injected-object
* @param injectedElement {@link InjectionMetadata.InjectedElement}
* @return
* @throws Exception
*/
@Override
protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
// 构建ReferenceBean名称
String referencedBeanName = buildReferencedBeanName(reference, injectedType);
// 构建ReferenceBean对象
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader());
// 放入缓存
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 创建代理对象
Object proxy = buildProxy(referencedBeanName, referenceBean, injectedType);

return proxy;
}

doGetInjectedBean方法先是构建ReferenceBean对象,然后通过jdk动态代理返回一个代理对象。

在构建代理对象前,需要先创建一个ReferenceBeanInvocationHandler,接着handler会调用ReferenceBean#get方法,这个方法是服务订阅的核心方法,下篇文章会讲解。得到InvocationHandler对象后,就通过jdk动态代理返回代理对象。

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码// ReferenceAnnotationBeanPostProcessor#buildProxy
private Object buildProxy(String referencedBeanName, ReferenceBean referenceBean, Class<?> injectedType) {
InvocationHandler handler = buildInvocationHandler(referencedBeanName, referenceBean);
Object proxy = Proxy.newProxyInstance(getClassLoader(), new Class[]{injectedType}, handler);
return proxy;
}

// ReferenceAnnotationBeanPostProcessor#buildInvocationHandler
private InvocationHandler buildInvocationHandler(String referencedBeanName, ReferenceBean referenceBean) {
// 先从缓存获取
ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.get(referencedBeanName);
if (handler == null) {
handler = new ReferenceBeanInvocationHandler(referenceBean);
}
if (applicationContext.containsBean(referencedBeanName)) { // Is local @Service Bean or not ?
// ReferenceBeanInvocationHandler's initialization has to wait for current local @Service Bean has been exported.
// 本地服务先缓存,等服务暴露再初始化
localReferenceBeanInvocationHandlerCache.put(referencedBeanName, handler);
} else {
// Remote Reference Bean should initialize immediately
// 远程服务需要马上初始化,调用ReferenceBeanInvocationHandler的init方法
handler.init();
}
return handler;
}

ReferenceBeanInvocationHandler源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
java复制代码private static class ReferenceBeanInvocationHandler implements InvocationHandler {

private final ReferenceBean referenceBean;

private Object bean;

private ReferenceBeanInvocationHandler(ReferenceBean referenceBean) {
this.referenceBean = referenceBean;
}

// 代理对象执行目标方法时,会被invoke拦截
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
if (bean == null) { // If the bean is not initialized, invoke init()
// issue: https://github.com/apache/incubator-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() {
// 调用ReferenceBean的get方法,进入真正的服务订阅过程
this.bean = referenceBean.get();
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%