当前位置: 首页 > 工具软件 > reference > 使用案例 >

Dubbo之@Reference 和 ReferenceBean

隆安然
2023-12-01

consumer调用dubbo服务

两种方法:

1、构建一个ReferenceBean,然后使用Spring的@Autowired引用服务

@Bean
public ReferenceBean<PLRepaymentPlanService> repaymentPlanServiceReferenceBean(){
    ReferenceBean<PLRepaymentPlanService> referenceBean = new ReferenceBean<>();
    referenceBean.setInterface(PLRepaymentPlanService.class);
    referenceBean.setCheck(false);
    referenceBean.setValidation("true");
    return referenceBean;
}
@Autowired
private PLRepaymentPlanService plRepaymentPlanService;

2、使用dubbo的@Reference注解

@Reference
ExampleService exampleService

@Reference其实就是将引用的服务 构建成一个ReferenceBean然后在调用 原理一样 

以下资料参考:https://blog.csdn.net/u012394095/article/details/83142193 

源码入口
com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScanRegistrar

Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  // 获取扫描包路径
  Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
  // 注册@service解析的类
  registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
  // 注册解析@Reference注解的bean
  registerReferenceAnnotationBeanPostProcessor(registry);

}



registerReferenceAnnotationBeanPostProcessor

 

private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {

        // Register @Reference Annotation Bean Processor
        BeanRegistrar.registerInfrastructureBean(registry,
                ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);

}

调用了BeanRegistrar工具类来注册Reference解析器的BeanDefinition, registerInfrastructureBean方法的主要作用就是将ReferenceAnnotationBeanPostProcessor这个类注册到BeanDefinition

public class BeanRegistrar {

    /**
     * Register Infrastructure Bean
     *
     * @param beanDefinitionRegistry {@link BeanDefinitionRegistry}
     * @param beanType               the type of bean
     * @param beanName               the name of bean
     */
    public static void registerInfrastructureBean(BeanDefinitionRegistry beanDefinitionRegistry,
                                                  String beanName,
                                                  Class<?> beanType) {

        if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
              // 注册
            beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
        }

    }

}


ReferenceAnnotationBeanPostProcessor


本文的重点在于ReferenceAnnotationBeanPostProcessor类,该类继承了InstantiationAwareBeanPostProcessor ,用来解析@Reference注解并完成依赖注入。

public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware,
        DisposableBean {
    // 省略注解
}

**InstantiationAwareBeanPostProcessor **:实例化Bean后置处理器(继承BeanPostProcessor)

1.postProcessBeforeInstantiation :在实例化目标对象之前执行,可以自定义实例化逻辑,如返回一个代理对象等。

2.postProcessAfterInitialization : Bean实例化完毕后执行的后处理操作,所有初始化逻辑、装配逻辑之前执行,如果返回false将阻止其他的InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation的执行。

3.postProcessPropertyValues :完成其他定制的一些依赖注入和依赖检查等,如AutowiredAnnotationBeanPostProcessor执行@Autowired注解注入,CommonAnnotationBeanPostProcessor执行@Resource等注解的注入,PersistenceAnnotationBeanPostProcessor执行@ PersistenceContext等JPA注解的注入,RequiredAnnotationBeanPostProcessor执行@ Required注解的检查等等。

dubbo也是采用了和@Autowired注入一样的原理,通过继承InstantiationAwareBeanPostProcessor 重写postProcessPropertyValues 方法来达到解析@Reference并实现依赖注入。

@Override
public PropertyValues postProcessPropertyValues(
  PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
  //这个是注入元数据,包含了目标Bean的Class对象,和注入元素(InjectionElement)集合
  InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs);
  try {
     // 通过反射来给bean设置值了
    metadata.inject(bean, beanName, pvs);
  } catch (BeanCreationException ex) {
    throw ex;
  } catch (Throwable ex) {
    throw new BeanCreationException(beanName, "Injection of @Reference dependencies failed", ex);
  }
  return pvs;
}


通过findReferenceMetadata找到@Reference,并解析得到元数据对象,最终实现依赖注入,@Autowired注解也是这个干的,二者的实现原理是一模一样。

private InjectionMetadata findReferenceMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
        // 通过类名作为缓存的key
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        // 从缓存中的injectionMetadataCache根据类名获取元数据
        ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
          // 判断metadata 是否为空,  class对象不等于ReferenceInjectionMetadata , 则需要进行刷新
        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 {
                          // 构建InjectionMetadata元数据
                        metadata = buildReferenceMetadata(clazz);
                        this.injectionMetadataCache.put(cacheKey, metadata);
                    } catch (NoClassDefFoundError err) {
                        throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
                                "] for reference metadata: could not find class that it depends on", err);
                    }
                }
            }
        }
        return metadata;
    }
 


buildReferenceMetadata

 

private ReferenceInjectionMetadata buildReferenceMetadata(final Class<?> beanClass) {
          // 获取属性上的@Reference注解
        Collection<ReferenceFieldElement> fieldElements = findFieldReferenceMetadata(beanClass);
          // 获取方法上的@Reference注解
        Collection<ReferenceMethodElement> methodElements = findMethodReferenceMetadata(beanClass);
        return new ReferenceInjectionMetadata(beanClass, fieldElements, methodElements);

}

获取属性上的@Reference注解findFieldReferenceMetadata

private List<ReferenceFieldElement> findFieldReferenceMetadata(final Class<?> beanClass) {
    
        final List<ReferenceFieldElement> elements = new LinkedList<ReferenceFieldElement>();
        // 通过反射的工具类,获取当前beanClass的所有Filed
        ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                // 获取Reference注解
                Reference reference = getAnnotation(field, Reference.class);
                // 注解不为空
                if (reference != null) {

                    if (Modifier.isStatic(field.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("@Reference annotation is not supported on static fields: " + field);
                        }
                        return;
                    }
                    // 构建ReferenceFieldElement
                    elements.add(new ReferenceFieldElement(field, reference));
                }

            }
        });

        return elements;

    }

上面的代码就很简单了,通过ReflectionUtils工具类,反射获取当前beanClass 的所有Filed , 之后获取每个filed上的@Reference注解,如果获取不为空,则继续下一步。最终构建ReferenceFieldElement对象,将对应的filed和Reference注解放进去。

元数据收集好了,接下来就是调用metadata.inject(bean, beanName, pvs);这个方法了。

public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
          // 获取InjectedElement
        Collection<InjectionMetadata.InjectedElement> elementsToIterate = this.checkedElements != null ? this.checkedElements : this.injectedElements;
        if (!((Collection)elementsToIterate).isEmpty()) {
            boolean debug = logger.isDebugEnabled();
            // 进行循环,也就是循环设值,因为有多个字段嘛。
            InjectionMetadata.InjectedElement element;
            for(Iterator var6 = ((Collection)elementsToIterate).iterator(); var6.hasNext(); element.inject(target, beanName, pvs)) {
                element = (InjectionMetadata.InjectedElement)var6.next();
                if (debug) {
                    logger.debug("Processing injected element of bean '" + beanName + "': " + element);
                }
            }
        }

    }

上面的代码,其实只有一行,那就是element.inject(target, beanName, pvs) 这一行,因为一个InjectedElement对象就表示一个字段对象,这个对象中将字段信息和注解信息绑定在了一起,调用inject方法就是为了给这个filed进行赋值。

 

###ReferenceFieldElement

 

private class ReferenceFieldElement extends InjectionMetadata.InjectedElement {
        // 字段对象
        private final Field field;
        // Reference注解对象
        private final Reference reference;
        // 服务引用对象
        private volatile ReferenceBean<?> referenceBean;

        protected ReferenceFieldElement(Field field, Reference reference) {
            super(field, null);
            this.field = field;
            this.reference = reference;
        }

        @Override
        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
            // 获取字段的类型
            Class<?> referenceClass = field.getType();
            // 构建ReferenceBean
            referenceBean = buildReferenceBean(reference, referenceClass);
            // 字段为私有,需要设置这个属性field.setAccessible(true)  才能进行设值
            ReflectionUtils.makeAccessible(field);
            // 给这个对象bean的这个filed设置值,值为:referenceBean.getObject()
            field.set(bean, referenceBean.getObject());

        }

    }

通过buildReferenceBean方法创建服务引用对象

private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> referenceClass) throws Exception {
        // 获取服务引用对象的缓存key
        String referenceBeanCacheKey = generateReferenceBeanCacheKey(reference, referenceClass);
        // 从缓存map中获取服务引用对象
        ReferenceBean<?> referenceBean = referenceBeansCache.get(referenceBeanCacheKey);

        if (referenceBean == null) {
            // 如果引用对象为空,则需要当场创建一个
            ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                    .create(reference, classLoader, applicationContext)
                    .interfaceClass(referenceClass);

            referenceBean = beanBuilder.build();
            //并且放入到缓存map中。
            referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean);

        }

        return referenceBean;

    }
 private String generateReferenceBeanCacheKey(Reference reference, Class<?> beanClass) {
        // 获取接口名称
        String interfaceName = resolveInterfaceName(reference, beanClass);
        // 通过引用的URl+接口名+接口版本号+接口分组,用来做缓存key
        String key = reference.url() + "/" + interfaceName +
                "/" + reference.version() +
                "/" + reference.group();

        Environment environment = applicationContext.getEnvironment();

        key = environment.resolvePlaceholders(key);

        return key;

    }

消费者每引用的一种服务,都会创建一个ReferenceBean, 如果多个地方使用@Reference引用同一个服务,需要看他们的的缓存key是否一样,如果都是一样的,那么就只会创建一个ReferenceBean,如果有些配置不一样,比如版本号不一致,则会创建创建不同的ReferenceBean对象,这也是他版本号能够起到的作用把。至此,@Reference注解已经解析完毕,并且服务引用的对象也已经创建了。

 类似资料: