【Dubbo学习系列】Dubbo消费端的代理生成详解(@DubboReference和@Reference)

文章目录

  • Spring中Dubbo实现RPC
    • 如何加载@Reference和@DubboReference注解
      • DubboComponentScanRegistrar
        • 概括
      • 发现@Refrence和@DubboReference注解,并编织RPC通信逻辑
        • ReferenceAnnotationBeanPostProcessor(核心)
          • @Reference
          • @DubboReference
          • @Reference和@DubboReference的区别
          • @DubboReference的使用方式
  • 总结

Spring中Dubbo实现RPC

在使用dubbo的过程中,我们的用法是在Service中引入 远程接口,然后标记上注解写入名称与版本号等一系列配置信息,然后在需要进行RPC通信的地方调用接口方法,然后就能获取到信息了。
那么按照这种思路,就应该有下列几个步骤需要完成

  1. 发现使用dubbo注解的成员与方法并托管于spring容器
  2. 在对象实例中找到有指定注解的成员变量或方法
  3. 通过动态代理的方式,编织通信代码到访问逻辑中

当然,上述是主要脉络,细分的话还有 序列化问题,地址的负载均衡问题,访问策略问题等等,本文主要讲述的就是上面的主要脉络。

这里主要是对@Reference和@DubboReference不同点做分析

更好的表述其实应该是新老版本的区别,这里@Reference就代指于旧版本2.7.7以下,@DubboReference就代指新版本2.7.7以上。后文也以@Reference和@DubboReference区分新旧

如何加载@Reference和@DubboReference注解

注意,@Reference已被废弃,2.7.7后使用@DubboReference

在springboot中我们会在启动类上添加 @EnableDubbo这个注解,而这个注解中有两个功能性注解 @EnableDubboConfig
@DubboComponentScan
前者负责dubbo配置的初始化,后者负责扫描dubbo配置中指定的包范围,也就是DubboComponentScanRegistrar.class类的职责

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
}
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
}

DubboComponentScanRegistrar

在源码中,作者标注出了这个类需要关注的几个关键类和注解

/*** Dubbo {@link DubboComponentScan} Bean Registrar** @see Service  注解,老版本dubbo标注的服务方Service注解* @see DubboComponentScan  注解,用于指定dubbo的管理范围,也就是扫描范围* @see ImportBeanDefinitionRegistrar  自身实现的接口,在spring的bean定义注册时期进行处理逻辑* @see ServiceAnnotationPostProcessor 对扫描路径的处理* @see ReferenceAnnotationBeanPostProcessor 处理dubbo注解,并生成代理类,【主要关注点】* @since 2.5.7*/public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {}

主要关注registerBeanDefinitions方法

    @Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// initialize dubbo beans//初始化信息DubboSpringInitializer.initialize(registry);//获取需要扫描的包路径,这个方法主要关注点就是,包路径的优先级关系与默认值问题Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);//为后续扫描上面获取的包路径做准备registerServiceAnnotationPostProcessor(packagesToScan, registry);}

先来看看第二步包路径优先级问题 ,这个问题很简单

  • 先看@DubboComponentScan
  • 在看@EnableDubbo
  • 最后如果都没有,则选用启动类的包路径

所以一般我们不设置包路径就是因为默认会扫描 启动类的包路径,也就会查询我们项目下面所有的类

   private Set<String> getPackagesToScan(AnnotationMetadata metadata) {// get from @DubboComponentScanSet<String> packagesToScan = getPackagesToScan0(metadata, DubboComponentScan.class, "basePackages", "basePackageClasses");// get from @EnableDubbo, compatible with spring 3.xif (packagesToScan.isEmpty()) {packagesToScan = getPackagesToScan0(metadata, EnableDubbo.class, "scanBasePackages", "scanBasePackageClasses");}if (packagesToScan.isEmpty()) {return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));}return packagesToScan;}

第三步,就是创建ServiceAnnotationPostProcessor类的bean定义,然后将其注册到spring中

    private void registerServiceAnnotationPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {//针对ServiceAnnotationPostProcessor类,创建bean定义BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationPostProcessor.class);//添加构造参数为包路径builder.addConstructorArgValue(packagesToScan);builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);}

包路径的扫描,过滤等处理就交由ServiceAnnotationPostProcessor来向下进行,后续就不在展开讲解该类了,有兴趣的可以自行查看。

概括

DubboComponentScanRegistrar类主要目的就是,将托管范围内的类,并入spring中。

发现@Refrence和@DubboReference注解,并编织RPC通信逻辑

上述流程完成了第一步,将dubbo的相关注解纳入spring的管理,下一步就是借助spring来发现相关注解并进行一些逻辑的编织生成代理对象。

ReferenceAnnotationBeanPostProcessor(核心)

该类是重点类,是dubbo逻辑编织处理类,这里要说明下,@Reference和@DubboReference的处理是有很大区别的(新老版本),背后的思想方式很有借鉴性。

/**
* 该类继承AbstractAnnotationBeanPostProcessor,核心逻辑分为两步
* 第一,发现被dubbo注解标识的成员变量,方法,或者类
* 第二,生成动态代理类,注入成员变量或方法,或者生成类(Bean)放入spring容器
**/
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor{
}
@Reference

这里 @Reference 发现主要借助于,postProcessMergedBeanDefinition方法在Bean创建之前合并定义的时候进行判断该类中是否有指定注解,如果有则持有其反射对象,待后面通过 postProcessPropertyValues 统一处理 (这种方式,在spring中与使用@Value注解流程一致)。

下面是第一步 postProcessMergedBeanDefinition的处理流程:

    @Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {if (beanType != null) {if (isReferenceBean(beanDefinition)) {...} else if (isAnnotatedReferenceBean(beanDefinition)) {...} else {//上面几个判断主要是对于@DubboReference的处理//第一步,找到当前类中使用@Reference的成员变量和方法AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);try {//第二步,放入容器中prepareInjection(metadata);} catch (Exception e) {throw new IllegalStateException("Prepare dubbo reference injection element failed", e);}}}}

而容器就是该类上面的两个成员变量

	//管理成员变量private final ConcurrentMap<InjectionMetadata.InjectedElement, String> injectedFieldReferenceBeanCache =new ConcurrentHashMap<>(CACHE_SIZE);//管理方法private final ConcurrentMap<InjectionMetadata.InjectedElement, String> injectedMethodReferenceBeanCache =new ConcurrentHashMap<>(CACHE_SIZE);

这里有个注意点,对于静态成员变量dubbo是不会进行处理的

    private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {...if (Modifier.isStatic(field.getModifiers())) {if (logger.isWarnEnabled()) {logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field);}return;}...}

如果需要设置为静态成员变量,只能通过访问方法,然后在其中设置,注意:这种使用方式,将会使你RPC通信内部的信息共享,导致不可预见的问题,请避免使用。

到这里,需要进行反射持有的对象已经放入容器中了,下一步就行进行代理生成并注入 。

下面是 postProcessPropertyValues的处理流程 , 也是两步
第一步,从容器中获取反射对象,如果没有,则重复发现步骤
第二步,就是进行动态代理,并注入

    @Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {try {//第一步,从容器中获取反射对象,如果没有,则重复发现步骤AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);prepareInjection(metadata);//第二步进行注入metadata.inject(bean, beanName, pvs);} catch (BeansException ex) {throw ex;} catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()+ " dependencies is failed", ex);}return pvs;}

而第二步方法主要是调用实现父类的doGetInjectedBean方法,注意:下面是旧版本中的代码

    @Overrideprotected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class<?> injectedType,InjectionMetadata.InjectedElement injectedElement) throws Exception {String referencedBeanName = buildReferencedBeanName(reference, injectedType);//生成ReferenceBean,这个bean是一个FactoryBean,这里并没有将其托管与Spring容器ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader());cacheInjectedReferenceBean(referenceBean, injectedElement);//生成代理类Object proxy = buildProxy(referencedBeanName, referenceBean, injectedType);//返回该对象return proxy;}

到此,注解对象就注入完毕了。

@DubboReference

而 @DubboReference 的发现则需要借助于spring容器,通过手动创建ReferenceBean托管于Spring容器,后续通过spring的 @Autowired 注解获取实例 详细请看@DubboReference的注释

在DubboReference中呢,也兼容了老版本的注解 ,doGetInjectedBean 变更为了直接从BeanFactory中获取

    @Overrideprotected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,AnnotatedInjectElement injectedElement) throws Exception {if (injectedElement.injectedObject == null) {throw new IllegalStateException("The AnnotatedInjectElement of @DubboReference should be inited before injection");}return getBeanFactory().getBean((String) injectedElement.injectedObject);}

在老注解@Reference不变的情况下,只需要自己的Configuration类中注入对应的ReferenceBean就OK了。

@Reference和@DubboReference的区别

这里其实已经可以看出明显思路的变化了,@Reference设计上是游离在Spring容器之外的,直接通过反射的方式进行增强,不依托于spring的容器,而 @DubboReference 则通过将FactoryBean融入到Spring环境中,真正的像使用本地方法一样,进行远程访问。

@DubboReference的使用方式

参考@DubboReference注解上面的注释就好了。

* Step 1: Register ReferenceBean in Java-config class:* <pre class="code">* @Configuration* public class ReferenceConfiguration {*     @Bean*     @DubboReference(group = "demo")*     public ReferenceBean<HelloService> helloService() {*         return new ReferenceBean();*     }**     @Bean*     @DubboReference(group = "demo", interfaceClass = HelloService.class)*     public ReferenceBean<GenericService> genericHelloService() {*         return new ReferenceBean();*     }* }* </pre>** Step 2: Inject ReferenceBean by @Autowired* <pre class="code">* public class FooController {*     @Autowired*     private HelloService helloService;**     @Autowired*     private GenericService genericHelloService;* }* </pre>

总结

依托于Spring容器的好处在于职责的划分更加清晰,dubbo本身只负责于rpc的通信,而bean的管理交还给spring,而且使ReferenceAnnotationBeanPostProcessor类更加轻,dubbo的关注点更加集中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554744.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java编程】之 List 与数组之间的相互转换Arrays.asList 和 ArrayList 的 subList

文章目录一、数组转换成 List 集合二、List 集合转数组浅谈 Arrays.asList1、不能往 Arrays.asList 生成的 List 集合中添加元素Arrays.asList 只是简单的充当转换接口二、浅谈 ArrayList 的 subList1、subList 索引的取值边界2、subList 不可强转成 ArrayList3、对 subList 的…

谈Java集合类的toArray()的小bug

谈Java集合类的toArray()的小bug toArray()方法 它的作用是将集合转换成数组。但是这个方法有一个弊端&#xff0c;当toArray()方法使用不当时会产生ClassCastException&#xff08;类转换异常&#xff09; public static void main(String[] args) {List<Integer> li…

Dubbo系统里面MultipartFile文件传输问题Dubbo文件上传/传输服务

今天遇到一个问题&#xff0c;在Controller 层里面&#xff0c;直接使用MultipartFile 来接收上传的图片&#xff0c;遇到几个坑。 一、在spring配置文件里面配置文件上传 <!--文件上传--><bean name"multipartResolver"class"org.springframework.web…

Dubbo2.7文档详解

本篇博文参考dubbo官方文档 本编博文参考javaguide之rpc 文章目录一.RPC1.1 什么是 RPC?1.2 为什么要用 RPC?1.3 RPC 能帮助我们做什么呢&#xff1f;1.4 RPC 的原理是什么?1.5 常见的 RPC 框架总结二.既有 HTTP ,为啥用 RPC 进行服务调用?2.1 RPC只是一种设计而已2.2 HTTP…

12nm 制程、Zen+ 微架构 AMD Ryzen 7 2700X 处理器详细测试 - 电脑领域 HKEPC Hard

12nm 制程、Zen 微架构 AMD Ryzen 7 2700X 处理器详细测试 代号 Pinnacle Ridge、AMD 第二代 Ryzen 处理器正式登场&#xff0c;基于经改良的 Zen 微架构&#xff0c;改善了 Cache 及记忆体延迟表现&#xff0c;更先进的 12nm LP 制程&#xff0c;令核心时脉进一步提升&#…

Java之Serializable接口实现序列化和反序列化实例以及部分序列化的四种方法

首先需要明确的概念: 序列化&#xff1a;将数据结构或对象转换成二进制字节流的过程反序列化&#xff1a;将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程持久化&#xff1a;将数据写入文件中长久保存的过程称之为持久化序列化主要目的&#xff1a;是…

win10死机频繁怎么解决

Windows 10已经推出好几年&#xff0c;系统趋于稳定&#xff0c;但依旧不是完美&#xff0c;蓝屏、死机状态还是会出现&#xff0c;只是概率降低了很多&#xff0c;如果你的电脑遇到了突然死机或者频繁卡死的情况&#xff0c;或许你应该考虑对电脑进行重置了。系统自带的恢复重…

Jar包常见的反编译工具介绍与使用

反编译JAR能干什么: 排查问题、分析商业软件代码逻辑&#xff0c;学习优秀的源码思路。 反编译工具介绍 JD-GUI 下载地址&#xff1a;http://java-decompiler.github.io/ 点评&#xff1a;支持的java版本不会太高&#xff0c;中文注释能够正常显示。 Luyten 下载地址&#…

400 bad request的原因意思和解决方法

我们的电脑在使用的过程中&#xff0c;有的小伙伴在上网的时候可能就遇到过系统提示&#xff1a;400 bad request的情况。据小编所知这种情况&#xff0c;大致意思就是出现了错误的请求或者请求不能满足。原因是因为我们请求的语法格式出现呢错误或者其他情况等等。我们可以通过…

final关键字的作用 (修改方法、变量、类)

文章目录前言final关键字的含义?什么是final变量&#xff1f;什么是final方法?什么是final类&#xff1f;final关键字的好处关于final的重要知识点前言 Java中的final关键字非常重要&#xff0c;它可以应用于类、方法以及变量。这篇文章中我将带你看看什么是final关键字&…

重装系统win10提示磁盘布局不受UEFI固件支持怎么办

原因分析&#xff1a; Win10系统新增UEFI检测机制&#xff0c;在BIOS开启了UEFI时&#xff0c;如果硬盘分区表格式不是GPT&#xff0c;则会提示无法重装系统win10&#xff0c;也就是说UEFIGPT或LegacyMBR才能安装win10。 解决方法一&#xff1a;关闭UEFI 1、重启系统时按Del…

win7按f8后没有进入安全模式怎么解决

win7f8后没有进入安全模式&#xff0c;在正确操作按F8没有进入安全模式之后&#xff0c;不知道怎么解决win7按f8后没有进入安全模式怎么解决&#xff0c;其实非常的简单&#xff0c;下面来看看详细的解决方法吧。 win7按f8后没有进入安全模式怎么解决 第一种方法&#xff1a;…

使用toArray将List转数组:使用Call to ‘toArray()‘ with pre-sized array argument ‘new String[list.size()]‘

代码示例&#xff1a; public static void main(String[] args) {List<String> list new ArrayList<String>();list.add("java");list.add("flink");list.add("spring");//第一种写法&#xff1a;该写法不规范String[] array1 li…

电脑开机就进入bios的解决方法

最近很多人反映自己的电脑一开机就直接进入bios里&#xff0c;无法正常进入系统。这是怎么回事呢?开机进入bios无法进入系统怎么办呢?别着急&#xff0c;今天就为大家带来电脑开机就进入bios的解决方法。 电脑开机就进入bios的解决方法&#xff1a; 1、如果是电脑的硬盘出了…

Serializable序列化和Externalizable序列化与反序列化的使用

1、序列化是干啥用的&#xff1f; 序列化的原本意图是希望对一个Java对象作一下“变换”&#xff0c;变成字节序列&#xff0c;这样一来方便持久化存储到磁盘&#xff0c;避免程序运行结束后对象就从内存里消失&#xff0c;另外变换成字节序列也更便于网络运输和传播&#xff…

Win10系统如何查看电脑是否是UEFI启动模式

Win10系统如何查看电脑是否是UEFI启动模式?现在越来越多的新电脑都采用UEFI来引导电脑系统&#xff0c;UEFI提高了开机后操作系统的启动速度&#xff0c;使电脑更加流畅&#xff0c;安全性更强&#xff0c;而传统的BIOS则没有UEFI用起来那么好。如何查看自己Win10系统电脑是否…

win10如何关闭Windows Defender安全保护程序

win10如何关闭Windows Defender安全保护程序&#xff1f;win10如何关闭Windows Defender安全保护程序?在安装的windows操作系统中&#xff0c;自带着安全保护程序“Windows Defender应用”&#xff0c;其实主要是为了保护好其他运行的软件&#xff0c;那该怎么来关闭这个软件呢…

7种抓包工具详细介绍

在处理IP网络的故障时&#xff0c;经常使用以太网抓包工具来查看和抓取IP网络上某些端口或某些网段的数据包&#xff0c;并对这些数据包进行分析&#xff0c;定位问题。 在 IMON项目里&#xff0c;使用抓包工具抓包进行分析的场景在EPG采集、引流模块和软终端监看模块&#xf…

2020-12-11

bios怎么恢复出厂设置? 1 第一步 打开电脑 在电脑出现开机提示时按下热键(一般是del或F2)就可以进入到bios设置界面 2 第二步 在bios设置界面中找到并选择“Load Optimized Default”&#xff0c; 然后按下回车键 3 第三步 在弹出窗口中选择“OK“ 然后回车键确定即可 …

JAVA序列化和反序列化的底层实现原理解析 更新时间:2019年11月27日 14:38:39 作者:myseries 这篇文章主要介绍了JAVA序列化和反序列化的底层实现原理解析,文中通

一、基本概念 1、什么是序列化和反序列化 &#xff08;1&#xff09;Java序列化是指把Java对象转换为字节序列的过程&#xff0c;而Java反序列化是指把字节序列恢复为Java对象的过程&#xff1b; &#xff08;2&#xff09;序列化&#xff1a;对象序列化的最主要的用处就是在…