Dubbo 第三节_ Dubbo的可扩展机制SPI源码解析

简介: Dubbo会对DubboProtocol对象进⾏依赖注⼊(也就是⾃动给属性赋值,属性的类型为⼀个接⼝,记为A接⼝),这个时候,对于Dubbo来说它并不知道该给这个属性赋什么值,换句话说,Dubbo并不知道在进⾏依赖注⼊时该找⼀个什么的的扩展点对象给这个属性,这时就会预先赋值⼀个A接⼝的⾃适应扩展点实例,也就是A接⼝的⼀个代理对象。在调⽤getExtension去获取⼀个扩展点实例后,会对实例进⾏缓存,下次再获取同样名字的扩展点实例时就会从缓存中拿了。Protocol是⼀个接。但是,不是只要在⽅法上加了。

第三节_ Dubbo的可扩展机制SPI源码解析


文章目录


正文

Dubbo SPI 架构图

Demo

1 ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExt
ensionLoader(Protocol.class);
2 Protocol http = extensionLoader.getExtension("dubbo");
3 System.out.println(http);

上⾯这个Demo就是Dubbo常⻅的写法,表示获取"dubbo"对应的Protocol扩展点。Protocol是⼀个接

⼝。

在ExtensionLoader类的内部有⼀个static的ConcurrentHashMap,⽤来缓存某个接⼝类型所对应的

ExtensionLoader实例

ExtensionLoader

ExtensionLoader表示某个接⼝的扩展点加载器,可以⽤来加载某个扩展点实例。

在ExtensionLoader中除开有上⽂的static的Map外,还有两个⾮常重要的属性:

  1. Class<?> type:表示当前ExtensionLoader实例是哪个接⼝的扩展点加载器
  2. ExtensionFactory objectFactory:扩展点⼯⼚(对象⼯⼚),可以获得某个对象
    ExtensionLoader和ExtensionFactory的区别在于:
  3. ExtensionLoader最终所得到的对象是Dubbo SPI机制产⽣的
  4. ExtensionFactory最终所得到的对象可能是Dubbo SPI机制所产⽣的,也可能是从Spring容器中所获
    得的对象
    在ExtensionLoader中有三个常⽤的⽅法:
  5. getExtension(“dubbo”):表示获取名字为dubbo的扩展点实例
  6. getAdaptiveExtension():表示获取⼀个⾃适应的扩展点实例
  7. getActivateExtension(URL url, String[] values, String group):表示⼀个可以被url激活的扩展点
    实例,后⽂详细解释
    其中,什么是⾃适应扩展点实例?它其实就是当前这个接⼝的⼀个代理对象。
1 ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExt
ensionLoader(Protocol.class);
2 Protocol protocol = extensionLoader.getExtension("dubbo");

当我们调⽤上述代码,我们会将得到⼀个DubboProtocol的实例对象,但在getExtension()⽅法中,

Dubbo会对DubboProtocol对象进⾏依赖注⼊(也就是⾃动给属性赋值,属性的类型为⼀个接⼝,记为A接⼝),这个时候,对于Dubbo来说它并不知道该给这个属性赋什么值,换句话说,Dubbo并不知道在进⾏依赖注⼊时该找⼀个什么的的扩展点对象给这个属性,这时就会预先赋值⼀个A接⼝的⾃适应扩展点实例,也就是A接⼝的⼀个代理对象。

后续,在A接⼝的代理对象被真正⽤到时,才会结合URL信息找到真正的A接⼝对应的扩展点实例进⾏调⽤

getExtension(String name)⽅法

在调⽤getExtension去获取⼀个扩展点实例后,会对实例进⾏缓存,下次再获取同样名字的扩展点实例时就会从缓存中拿了。

createExtension(String name)⽅法

在调⽤createExtension(String name)⽅法去创建⼀个扩展点实例时,要经过以下⼏个步骤:

  1. 根据name找到对应的扩展点实现类
  2. 根据实现类⽣成⼀个实例,把实现类和对应⽣成的实例进⾏缓存
  3. 对⽣成出来的实例进⾏依赖注⼊(给实例的属性进⾏赋值)
  4. 对依赖注⼊后的实例进⾏AOP(Wrapper),把当前接⼝类的所有的Wrapper全部⼀层⼀层包裹在实例
    对象上,没包裹个Wrapper后,也会对Wrapper对象进⾏依赖注⼊
  5. 返回最终的Wrapper对象

getExtensionClasses

getExtensionClasses()是⽤来加载当前接⼝所有的扩展点实现类的,返回⼀个Map。之后可以从这个

Map中按照指定的name获取对应的扩展点实现类。

当把当前接⼝的所有扩展点实现类都加载出来后也会进⾏缓存,下次需要加载时直接拿缓存中的。

Dubbo在加载⼀个接⼝的扩展点时,思路是这样的:

  1. 根据接⼝的全限定名去META-INF/dubbo/internal/⽬录下寻找对应的⽂件,调⽤loadResource⽅法
    进⾏加载
  2. 根据接⼝的全限定名去META-INF/dubbo/⽬录下寻找对应的⽂件,调⽤loadResource⽅法进⾏加载
  3. 根据接⼝的全限定名去META-INF/services/⽬录下寻找对应的⽂件,调⽤loadResource⽅法进⾏加

    这⾥其实会设计到⽼版本兼容的逻辑,不解释了

loadResource⽅法

loadResource⽅法就是完成对⽂件内容的解析,按⾏进⾏解析,会解析出"=“两边的内容,”="左边的内容

就是扩展点的name,右边的内容就是扩展点实现类,并且会利⽤ExtensionLoader类的类加载器来加载扩

展点实现类。

然后调⽤loadClass⽅法对name和扩展点实例进⾏详细的解析,并且最终把他们放到Map中去。

loadClass⽅法

loadClass⽅法会做如下⼏件事情:

  1. 当前扩展点实现类上是否存在@Adaptive注解,如果存在则把该类认为是当前接⼝的默认⾃适应类(接⼝代理类),并把该类存到cachedAdaptiveClass属性上。
  2. 当前扩展点实现是否是⼀个当前接⼝的⼀个Wrapper类,如果判断的?就是看当前类中是否存在⼀个构造⽅法,该构造⽅法只有⼀个参数,参数类型为接⼝类型,如果存在这⼀的构造⽅法,那么这个类就是该接⼝的Wrapper类,如果是,则把该类添加到cachedWrapperClasses中去,
    cachedWrapperClasses是⼀个set。
  3. 如果不是⾃适应类,或者也不是Wrapper类,则判断是有存在name,如果没有name,则报错。
  4. 如果有多个name,则判断⼀下当前扩展点实现类上是否存在@Activate注解,如果存在,则把该类添加到cachedActivates中,cachedWrapperClasses是⼀个map。
  5. 最后,遍历多个name,把每个name和对应的实现类存到extensionClasses中去,extensionClasses
    就是上⽂所提到的map。
    ⾄此,加载类就⾛完了。
    回到createExtension(String name)⽅法中的逻辑,当前这个接⼝的所有扩展点实现类都扫描完了之后,就可以根据⽤户所指定的名字,找到对应的实现类了,然后进⾏实例化,然后进⾏IOC(依赖注⼊)和AOP。

Dubbo中的IOC

  1. 根据当前实例的类,找到这个类中的setter⽅法,进⾏依赖注⼊
  2. 先分析出setter⽅法的参数类型pt
  3. 在截取出setter⽅法所对应的属性名property
  4. 调⽤objectFactory.getExtension(pt, property)得到⼀个对象,这⾥就会从Spring容器或通过
    DubboSpi机制得到⼀个对象,⽐较特殊的是,如果是通过DubboSpi机制得到的对象,是pt这个类型
    的⼀个⾃适应对象(代理对象)。
  5. 再反射调⽤setter⽅法进⾏注⼊

Dubbo中的AOP

dubbo中也实现了⼀套⾮常简单的AOP,就是利⽤Wrapper,如果⼀个接⼝的扩展点中包含了多个

Wrapper类,那么在实例化完某个扩展点后,就会利⽤这些Wrapper类对这个实例进⾏包裹,⽐如:现在有⼀个DubboProtocol的实例,同时对于Protocol这个接⼝还有很多的Wrapper,⽐如

ProtocolFilterWrapper、ProtocolListenerWrapper,那么,当对DubboProtocol的实例完成了IOC之

后,就会先调⽤new ProtocolFilterWrapper(DubboProtocol实例)⽣成⼀个新的Protocol的实例,再对

此实例进⾏IOC,完了之后,会再调⽤new ProtocolListenerWrapper(ProtocolFilterWrapper实例)⽣成

⼀个新的Protocol的实例,然后进⾏IOC,从⽽完成DubboProtocol实例的AOP。

⾃适应扩展点补充

上⾯提到的⾃适应扩展点对象,也就是某个接⼝的代理对象是通过Dubbo内部⽣成代理类,然后⽣成代理

对象的。

额外的,在Dubbo中还设计另外⼀种机制来⽣成⾃适应扩展点,这种机制就是可以通过@Adaptive注解来

指定某个类为某个接⼝的代理类,如果指定了,Dubbo在⽣成⾃适应扩展点对象时实际上⽣成的就是

@Adaptive注解所注解的类的实例对象。

如果是由Dubbo默认实现的,那么我们就看看Dubbo是如何⽣成代理类的。

createAdaptiveExtensionClass⽅法

createAdaptiveExtensionClass⽅法就是Dubbo中默认⽣成Adaptive类实例的逻辑。说⽩了,这个实例

就是当前这个接⼝的⼀个代理对象。⽐如下⾯的代码:

1 ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExt
ensionLoader(Protocol.class);
2 Protocol protocol = extensionLoader.getAdaptiveExtension();

这个代码就是Protocol接⼝的⼀个代理对象,那么代理逻辑就是在new

AdaptiveClassCodeGenerator(type, cachedDefaultName).generate()⽅法中。

  1. type就是接⼝
  2. cacheDefaultName就是该接⼝默认的扩展点实现的名字
    看个例⼦,Protocol接⼝的Adaptive类:
1 package org.apache.dubbo.rpc;
2 import org.apache.dubbo.common.extension.ExtensionLoader;
3 public class Protocol$Adaptive implements org.apache.dubbo.rpc.Pr
otocol {
4
5 public void destroy() {
6 throw new UnsupportedOperationException("The method publi
c abstract void org.apache.dubbo.rpc.Protocol.destroy() of interf
ace org.apache.dubbo.rpc.Protocol is not adaptive method!");
7 }
8
9 public int getDefaultPort() {
10 throw new UnsupportedOperationException("The method publi
c abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of
interface org.apache.dubbo.rpc.Protocol is not adaptive method!"
);
11 }
12
13 public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.
rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
14 if (arg0 == null)
15 throw new IllegalArgumentException("org.apache.dubbo.
rpc.Invoker argument == null");
16 if (arg0.getUrl() == null)
17 throw new IllegalArgumentException("org.apache.dubbo.
rpc.Invoker argument getUrl() == null");
18
19 org.apache.dubbo.common.URL url = arg0.getUrl();
20
21 String extName = ( url.getProtocol() == null ? "dubbo" :
url.getProtocol() );
22
23 if(extName == null)
24 throw new IllegalStateException("Failed to get extens
ion (org.apache.dubbo.rpc.Protocol) name from url (" + url.toStri
ng() + ") use keys([protocol])");
25
26 org.apache.dubbo.rpc.Protocol extension = (org.apache.dub
bo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dub
bo.rpc.Protocol.class).getExtension(extName);
27
28 return extension.export(arg0);
29 }
30
31 public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg
0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.
RpcException {
32
33 if (arg1 == null) throw new IllegalArgumentException("url
== null");
34
35 org.apache.dubbo.common.URL url = arg1;
36
37 String extName = ( url.getProtocol() == null ? "dubbo" :
url.getProtocol() );
38
39 if(extName == null) throw new IllegalStateException("Fail
ed to get extension (org.apache.dubbo.rpc.Protocol) name from url
(" + url.toString() + ") use keys([protocol])");
40
41 org.apache.dubbo.rpc.Protocol extension = (org.apache.dub
bo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dub
bo.rpc.Protocol.class).getExtension(extName);
42
43 return extension.refer(arg0, arg1);
44 }
45 }

可以看到,Protocol接⼝中有四个⽅法,但是只有export和refer两个⽅法进⾏代理。为什么?因为

Protocol接⼝中在export⽅法和refer⽅法上加了@Adaptive注解。但是,不是只要在⽅法上加了

@Adaptive注解就可以进⾏代理,还有其他条件,⽐如:

  1. 该⽅法如果是⽆参的,那么则会报错
  2. 该⽅法有参数,可以有多个,并且其中某个参数类型是URL,那么则可以进⾏代理
  3. 该⽅法有参数,可以有多个,但是没有URL类型的参数,那么则不能进⾏代理
  4. 该⽅法有参数,可以有多个,没有URL类型的参数,但是如果这些参数类型,对应的类中存在getUrl⽅
    法(返回值类型为URL),那么也可以进⾏代理
    所以,可以发现,某个接⼝的Adaptive对象,在调⽤某个⽅法时,是通过该⽅法中的URL参数,通过调⽤
    ExtensionLoader.getExtensionLoader(com.luban.Car.class).getExtension(extName);得到⼀个扩展
    点实例,然后调⽤该实例对应的⽅法。

Activate扩展点

上⽂说到,每个扩展点都有⼀个name,通过这个name可以获得该name对应的扩展点实例,但是有的场景

下,希望⼀次性获得多个扩展点实例

demo

1 ExtensionLoader<Filter> extensionLoader = ExtensionLoader.getExte
nsionLoader(Filter.class);
2 URL url = new URL("http://", "localhost", 8080);
3 url = url.addParameter("cache", "test");
4
5 List<Filter> activateExtensions = extensionLoader.getActivateExte
nsion(url,
6 new String[
]{"validation"},
7 CommonConst
ants.CONSUMER);
8 for (Filter activateExtension : activateExtensions) {
9 System.out.println(activateExtension);
10 }

会找到5个Filter

1 org.apache.dubbo.rpc.filter.ConsumerContextFilter@4566e5bd
2 org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter@1ed4004b
3 org.apache.dubbo.monitor.support.MonitorFilter@ff5b51f
4 org.apache.dubbo.cache.filter.CacheFilter@25bbe1b6
5 org.apache.dubbo.validation.filter.ValidationFilter@5702b3b1

前三个是通过CommonConstants.CONSUMER找到的

CacheFilter是通过url中的参数找到的

ValidationFilter是通过指定的name找到的

在⼀个扩展点类上,可以添加@Activate注解,这个注解的属性有:

  1. String[] group():表示这个扩展点是属于哪组的,这⾥组通常分为PROVIDER和CONSUMER,表示
    该扩展点能在服务提供者端,或者消费端使⽤
  2. String[] value():表示的是URL中的某个参数key,当利⽤getActivateExtension⽅法来寻找扩展点
    时,如果传⼊的url中包含的参数的所有key中,包括了当前扩展点中的value值,那么则表示当前url可
    以使⽤该扩展点。
相关文章
|
12月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
470 4
|
12月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
12月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
325 2
|
Dubbo Java 应用服务中间件
💥Spring Cloud Dubbo火爆来袭!微服务通信的终极利器,你知道它有多强大吗?🔥
【8月更文挑战第29天】随着信息技术的发展,微服务架构成为企业应用开发的主流模式,而高效的微服务通信至关重要。Spring Cloud Dubbo通过整合Dubbo与Spring Cloud的优势,提供高性能RPC通信及丰富的生态支持,包括服务注册与发现、负载均衡和容错机制等,简化了服务调用管理并支持多种通信协议,提升了系统的可伸缩性和稳定性,成为微服务通信领域的优选方案。开发者仅需关注业务逻辑,而无需过多关心底层通信细节,使得Spring Cloud Dubbo在未来微服务开发中将更加受到青睐。
254 0
|
Dubbo 应用服务中间件 Apache
Star 4w+,Apache Dubbo 3.3 全新发布,Triple X 领衔,开启微服务通信新时代
在 Apache Dubbo 突破 4w Star 之际,Apache Dubbo 团队正式宣布,Dubbo 3.3 正式发布!作为全球领先的开源微服务框架,Dubbo 一直致力于为开发者提供高性能、可扩展且灵活的分布式服务解决方案。此次发布的 Dubbo 3.3,通过 Triple X 的全新升级,突破了以往局限,实现了对南北向与东西向流量的全面支持,并提升了对云原生架构的友好性。
449 95
|
负载均衡 Dubbo 应用服务中间件
框架巨擘:Dubbo如何一统异构微服务江湖,成为开发者的超级武器!
【8月更文挑战第8天】在软件开发中,微服务架构因灵活性和可扩展性备受欢迎。面对异构微服务的挑战,Apache Dubbo作为高性能Java RPC框架脱颖而出。它具备服务注册与发现、负载均衡及容错机制等核心特性,支持多种通信协议和序列化方式,能有效连接不同技术栈的微服务。Dubbo的插件化设计保证了面向未来的扩展性,使其成为构建稳定高效分布式系统的理想选择。
360 80
|
Dubbo 应用服务中间件 Apache
Star 4w+,Apache Dubbo 3.3 全新发布,Triple X 领衔,开启微服务通信新时代
Star 4w+,Apache Dubbo 3.3 全新发布,Triple X 领衔,开启微服务通信新时代
193 0
|
Dubbo Cloud Native 应用服务中间件
阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。
在云原生时代,微服务架构成为主流。阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。示例代码展示了如何在项目中实现两者的整合,通过 Nacos 动态调整服务状态和配置,适应多变的业务需求。
507 2

热门文章

最新文章

推荐镜像

更多
  • DNS