参考资料:Dubbo系列之(四)服务订阅(1)
介绍
dubbo的服务订阅有两种方式,第一种是通过xml文件的标签<dubbo:reference />
,第二种是通过注解@Reference
。两者在使用上没有什么区别,标签上的属性都可以在注解上找到对应的配置。在源码实现上,两者存在一定的区别和共同点。
共同点:两者最终都是调用com.alibaba.dubbo.config.ReferenceConfig#get
方法来产生代理对象和订阅服务
区别:
-
标签<dubbo:reference />的实现方式是通过spring自定义标签实现的。dubbo使用ReferenceBean解析标签内容,ReferenceBean实现了FactoryBean接口,因此可通过getObject方法创建具体的实例对象,该方法里面就是调用父类ReferenceConfig#get方法。
-
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
-
@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,源码如下:
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方法。
由于ReferenceAnnotationBeanPostProcessor没有实现postProcessPropertyValues方法,所以我们看到的是其父类AnnotationInjectedBeanPostProcessor
AnnotationInjectedBeanPostProcessor#postProcessPropertyValues
@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
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
/**
* 找到需要注入的成员元信息,封装成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
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
/**
* 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
/**
* 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
@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
@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
/**
* 该方法是一个模板方法,用来得到一个指定注入类型的对象
*
* @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动态代理返回代理对象。
源码如下:
// 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
源码如下
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();
}
}