改写Dubbo @Reference 注解

改写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;
  1. 重写ReferenceAnnotationBeanPostProcessor

    @Component
    public class MemberReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor<MemberReference>
            implements ApplicationContextAware, ApplicationListener {
    
  2. 修改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;
}
  1. 在目标服务上使用

    @MemberReference(timeout = 1000)
    private A a;
    

总结:

  1. @MemberReference注解 对@Reference 无任何影响,包含其所有功能;
  2. 在项目需要指定不同的服务策略时,而不需要生产者修改任何配置,就可以使用@MemberReference注解;
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值