改写Dubbo @Reference 注解
背景:
项目中想通过@Reference注解 多次引入某dubbo服务,比如在A业务场景中 dubbo服务的超时时间 是1000ms,B业务场景中dubbo服务的超时时间是3000ms ,都想让其生效,就目前的@Reference的功能,除非生产者进行版本划分,分组等操作,不然无法实现这个功能。
@Reference注解源码说明:
Spring中通过@Reference注入某个dubbo服务。
入口是ReferenceAnnotationBeanPostProcessor,这是一个InstantiationAwareBeanPostProcessor,也是一个MergedBeanDefinitionPostProcessor,这两个Processor都是BeanPostProcessor,这里提到是因为有重大用途。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean,在这个方法中会执行这个代码,
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
ReferenceAnnotationBeanPostProcessor 的父类(AnnotationInjectedBeanPostProcessor )实现了postProcessPropertyValues 这个方法,在这个方法中会获取之前通过实现postProcessMergedBeanDefinition方法构造的InjectionMetadata,调用inject方法
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs); ***************** 重要
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName()
+ " dependencies is failed", ex);
}
return pvs;
}
inject方法就会走到这里com.alibaba.dubbo.config.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor#getInjectedObject
protected Object getInjectedObject(A annotation, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
String cacheKey = buildInjectedObjectCacheKey(annotation, bean, beanName, injectedType, injectedElement);
Object injectedObject = injectedObjectsCache.get(cacheKey);
if (injectedObject == null) {
injectedObject = doGetInjectedBean(annotation, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
@Override
protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
String referencedBeanName = buildReferencedBeanName(reference, injectedType); // 这里就可以知道 referencedBeanName 是由 @Reference 中的version 和 group 以及接口全路径等基础信息构成,这个将会作为这个服务的缓存key进行缓存。
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader());
cacheInjectedReferenceBean(referenceBean, injectedElement);
Object proxy = buildProxy(referencedBeanName, referenceBean, injectedType);
return proxy;
}
private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, Reference reference,
Class<?> referencedType, ClassLoader classLoader)
throws Exception {
ReferenceBean<?> referenceBean = referenceBeanCache.get(referencedBeanName); // 这也是为什么一个项目中多次通过 @Reference注解注入相同服务只有一个生效的原因。前提是没有指定version 和 group这个属性,但是通过指定这两个属性来实现 不同注入 还需要生产者那边 进行对应修改,这里也我改写@Reference注解的原因。
if (referenceBean == null) {
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(reference, classLoader, applicationContext)
.interfaceClass(referencedType);
referenceBean = beanBuilder.build();
referenceBeanCache.put(referencedBeanName, referenceBean);
}
return referenceBean;
}
改写开始
1.定义一个注解 包含@Reference 所有属性,增加一个 isSingleton的 属性
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface MemberReference {
boolean isSingleton() default false;
-
重写ReferenceAnnotationBeanPostProcessor
@Component public class MemberReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor<MemberReference> implements ApplicationContextAware, ApplicationListener {
-
修改buildReferenceBeanIfAbsent方法
private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, MemberReference reference,
Class<?> referencedType, ClassLoader classLoader)
throws Exception {
ReferenceBean<?> referenceBean = referenceBeanCache.get(referencedBeanName);
if (referenceBean == null || !reference.isSingleton()) { // 增加一个判断 即可
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(reference, classLoader, applicationContext)
.interfaceClass(referencedType);
referenceBean = beanBuilder.build();
referenceBeanCache.put(referencedBeanName, referenceBean);
}
return referenceBean;
}
-
在目标服务上使用
@MemberReference(timeout = 1000) private A a;
总结:
- @MemberReference注解 对@Reference 无任何影响,包含其所有功能;
- 在项目需要指定不同的服务策略时,而不需要生产者修改任何配置,就可以使用@MemberReference注解;