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注解已经解析完毕,并且服务引用的对象也已经创建了。

Dubbo之@Reference 和 ReferenceBean相关推荐

  1. dubbo的@Reference注解作用分析

    目的 看看dubbo是怎么给加了@Reference注解的属性注入invoker实例,为什么有时候加了@Reference注解的属性会是null. ReferenceAnnotationBeanPos ...

  2. Dubbo学习记录(八) -- Spring整合Dubbo中@Reference注解解析原理

    Spring整合Dubbo中@Reference注解解析原理 @Reference: 可以用在属性或者方法, 意味着需要引用某个Dubbo服务, 那么Dubbo整合Spring后, 我很好奇怎么把这个 ...

  3. Dubbo的Reference注解必须先启动provider的问题

    目录 现象 看源码分析原因 注解Reference第一步:用Reference注解里的参数初始化ReferenceConfig 注解Reference第二步:从配置文件里获取参数,写入Referenc ...

  4. Spring-Boot 整合Dubbo 解决@Reference 注解为null情况

    首先检查一下你的spring boot版本是多少? 如果是2.X 不用看了,spring boot 2.x 必定会出现这个问题, 改为 1.5.9 或其他1.x版本,目前生产环境建议使用1.x版本. ...

  5. Dubbo的@Reference和@Service说明

    前言 @Reference 用在消费端,表明使用的是服务端的什么服务 1 @RestController2 public class RemoteUserController {3 4 5 6 @Re ...

  6. dubbo学习篇1 注解之 @Reference 原理解析

    一. 使用注解 在dubbo springboot 使用时,在需要调用的服务接口上使用 @Reference 即可直接调用远程服务 @Reference(version = "1.0.0&q ...

  7. dubbo学习过程、使用经验分享及实现原理简单介绍

    一.前言 部门去年年中开始各种改造,第一步是模块服务化,这边初选dubbo试用在一些非重要模块上,慢慢引入到一些稍微重要的功能上,半年时间,学习过程及线上使用遇到的些问题在此总结下. 整理这篇文章差不 ...

  8. 改造Dubbo,使其可以对接口方法进行注解配置

    为什么80%的码农都做不了架构师?>>>    在之前的文章中介绍过了基于Spring 4 + Dubbo 的注解配置.参见<改造Dubbo,使其能够兼容Spring 4注解配 ...

  9. Dubbo学习总结(2)——Dubbo架构详解

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 部门去年年中开始各种改造,第一步是模块服务化,这边初选dubbo试用在一些非重要模块上,慢慢引入到一些稍微重要的功能 ...

最新文章

  1. 如何添加QQ 微信等程序到右键打开
  2. glassfish hk2_使用GlassFish 3.1.2.2和Primefaces 3.4的JDBC领域和基于表单的身份验证
  3. [渝粤教育] 西南科技大学 单片机原理与应用 在线考试复习资料(2)
  4. [转]Android的Handler总结
  5. SOA概念的三个比喻
  6. 【MFC开发(4)】按钮控件BUTTON
  7. WAP中推送技术的分析与设计
  8. 【JAVA程序设计】(C00019)javaweb高校社团管理系统
  9. 应用时间序列分析_时间序列-应用
  10. 数学最重要 :一个经济博士的总结(常春藤)Ph.D
  11. 计算机微信开发中期检查表,中期检查表范例
  12. SQL Server索引的维护 - 索引碎片、填充因子 第三篇
  13. 曲苑杂坛--数据库更新探秘
  14. 谷歌admob测试设备测试流程
  15. HDU 4939 Stupid Tower Defense 简单DP
  16. Windows xp IIS 信息服务组件安装包
  17. Linux下往github上传项目
  18. Mac电脑如何启用root用户
  19. 万马齐喑究可哀-中文编程的又一波quot;讨论quot;
  20. Nginx总结(2)—Nginx的反向代理

热门文章

  1. 初中数学最全几何模型_初中数学几何九大模型,看见的不能错过,收藏后考试必备...
  2. linux 中文 bterm fbterm 内核,完美中文tty, fbterm+yong(小小输入法)
  3. web前端技术——二、表格与框架
  4. 配电网正常重构,孤岛划分及故障重构
  5. FreeMarker导出Word
  6. Python爬虫爬取一篇韩寒新浪博客
  7. 木犀草素修饰人血清白蛋白(Luteolin-HSA),山柰酚修饰人血清白蛋白(Kaempferol-HSA)
  8. 流媒体服务器FreeSWITCH的安装、配置与启动
  9. Android获取本地相册图片
  10. 行测-判断推理-图形推理-样式规律-空间重构-四面体和八面体