Dubbo系列(二)源码分析之SPI机制

在阅读Dubbo源码时,常常看到

ExtensionLoader.getExtensionLoader(*.class).getAdaptiveExtension();

ExtensionLoader.getExtensionLoader(*.class).getExtension(“name”);

那么需要深入了解dubbo,了解SPI源码是必不可少的过程

下面根据dubbo 2.5.4进行源码分析

ps.由于源码分析过程不便于分段落,很容易混乱,需要根据源码信息一步一步追踪。

万恶之源——SPI入口

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {if(type == null) {throw new IllegalArgumentException("Extension type == null");} else if(!type.isInterface()) {throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");} else if(!withExtensionAnnotation(type)) {throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");} else {ExtensionLoader loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);if(loader == null) {EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);}return loader;}}

EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));

调用了构造器方法,并置入EXTENSION_LOADERS,那么EXTENSION_LOADERS可以推测出是一个容器,用来保存ExtensionLoader信息,不必每次都new,而便于调用。

    private ExtensionLoader(Class<?> type) {this.type = type;this.objectFactory = type == ExtensionFactory.class?null:(ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();}

那么就需要明白ExtensionLoader这个类实例化对象是用来干什么的,首先我们知道EXTENSION_LOADERS是用来保存type-ExtensionLoader信息的map,type是接口类类型,那么很容易联想到ExtensionLoader对象则是用来为该接口类生产不同用户所需的实现类的。

开始验证猜想:type是接口类类型,ExtensionLoader对象是用来为该接口类生产不同用户所需的实现类的。

ExtensionLoader.getExtensionLoader(*.class).getAdaptiveExtension();入手,看看是如何获取真正的实现类的。

getExtensionLoader(type)

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {if(type == null) {throw new IllegalArgumentException("Extension type == null");} else if(!type.isInterface()) {throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");} else if(!withExtensionAnnotation(type)) {throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");} else {ExtensionLoader loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);if(loader == null) {EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);}//最终返回一个EXTENSION_LOADERS已存在或新创建的new ExtensionLoader(type)对象return loader;}}

new ExtensionLoader(type)

    private ExtensionLoader(Class<?> type) {//实例化对象有两个属性,1.type 2.objectFactorythis.type = type;//objectFactory过滤了ExtensionFactory.class,那么进入:后面的语句this.objectFactory = type == ExtensionFactory.class?null:(ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();}

(ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();

(ExtensionFactory)getExtensionLoader(ExtensionFactory.class)是不是有熟悉的感觉

此时传入的类型为ExtensionFactory,调用方式与开场白没有区别!

那么再次猜想:ExtensionFactory是用来生产Extension的类,且也是通过SPI的方式获得的扩展类。

Extension是用来生产扩展类具体实现类的类,可能会有一点绕,不过对比spring中的beanFactoryFactoryBean还是比较好理解的。)

现在终于要进入getAdaptiveExtension()方法了

getAdaptiveExtension()

    public T getAdaptiveExtension() {//从缓存获取默认实现类Object instance = this.cachedAdaptiveInstance.get();if(instance == null) {if(this.createAdaptiveInstanceError != null) {throw new IllegalStateException("fail to create adaptive instance: " + this.createAdaptiveInstanceError.toString(), this.createAdaptiveInstanceError);}Holder var2 = this.cachedAdaptiveInstance;//使用双重检查来实例化synchronized(this.cachedAdaptiveInstance) {instance = this.cachedAdaptiveInstance.get();if(instance == null) {try {//实例化的方法instance = this.createAdaptiveExtension();this.cachedAdaptiveInstance.set(instance);} catch (Throwable var5) {this.createAdaptiveInstanceError = var5;throw new IllegalStateException("fail to create adaptive instance: " + var5.toString(), var5);}}}}return instance;}

继续进入createAdaptiveExtension()

    private T createAdaptiveExtension() {try {return this.injectExtension(this.getAdaptiveExtensionClass().newInstance());} catch (Exception var2) {throw new IllegalStateException("Can not create adaptive extenstion " + this.type + ", cause: " + var2.getMessage(), var2);}}

继续进入this.injectExtension(this.getAdaptiveExtensionClass().newInstance());

那么有两个过程,1.实例化对象 2.inject依赖注入 ,类似spring的依赖注入过程,毕竟bean都是由spring管理的

newInstance()就不深入看了,简单介绍就是通过反射的方式获取到第一个public构造器,并调用instance()方法进行该type 默认实现类的实例化。

为了验证是默认实现类,进入getAdaptiveExtensionClass()

    private Class<?> getAdaptiveExtensionClass() {this.getExtensionClasses();return this.cachedAdaptiveClass != null?this.cachedAdaptiveClass:(this.cachedAdaptiveClass = this.createAdaptiveExtensionClass());}

记住return的对象是cachedAdaptiveClasscreateAdaptiveExtensionClass()

继续getExtensionClasses();

    private Map<String, Class<?>> getExtensionClasses() {//这里缓存的是已加载类的类型Map classes = (Map)this.cachedClasses.get();if(classes == null) {Holder var2 = this.cachedClasses;synchronized(this.cachedClasses) {classes = (Map)this.cachedClasses.get();if(classes == null) {//加载扩展类classes = this.loadExtensionClasses();this.cachedClasses.set(classes);}}}return classes;}

继续深入loadExtensionClasses();

    private Map<String, Class<?>> loadExtensionClasses() {//回忆起猜想:默认//获取SPI注解SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);if(defaultAnnotation != null) {String extensionClasses = defaultAnnotation.value();if(extensionClasses != null && (extensionClasses = extensionClasses.trim()).length() > 0) {String[] names = NAME_SEPARATOR.split(extensionClasses);if(names.length > 1) {throw new IllegalStateException("more than 1 default extension name on extension " + this.type.getName() + ": " + Arrays.toString(names));}if(names.length == 1) {//缓存从SPI注解中获取的默认实现类名this.cachedDefaultName = names[0];}}}HashMap extensionClasses1 = new HashMap();//按照dubbo SPI规范加载这三个路径下的文件,并加入到extensionClasses1这个map中this.loadFile(extensionClasses1, "META-INF/dubbo/internal/");this.loadFile(extensionClasses1, "META-INF/dubbo/");this.loadFile(extensionClasses1, "META-INF/services/");return extensionClasses1;}

return extensionClasses1回溯后发现,此时这三个路径下的文件中配置的实现类都加载到cachedClasses中,且使得cachedDefaultName中有了值,作为默认实现类。

此时进入createAdaptiveExtensionClass()

    private Class<?> createAdaptiveExtensionClass() {//生成代码String code = this.createAdaptiveExtensionClassCode();//获得类加载器ClassLoader classLoader = findClassLoader();//获得编译器Compiler compiler = (Compiler)getExtensionLoader(Compiler.class).getAdaptiveExtension();//获得编译后的类return compiler.compile(code, classLoader);}

这里则是实例化实现类的核心方法了

可见,实例化实现类的核心方法首先是通过代码写代码!

那么生成的类是什么样的呢?

以protocol为例,最终生成Protocol$Adaptive类:

    public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {if (arg1 == null) throw new IllegalArgumentException("url == null");com.alibaba.dubbo.common.URL url = arg1;String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null)throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();//SPI中配置项为"dubbo",所以这里编译出来的默认值为dubboString extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());if (extName == null)throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);}}

难受的是,我们发现里面依然嵌套了一层extension的调用模式,说明这个类依然是一个代理类!

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

不过不一样的是,这里调用的方法是getExtension(extName),不要着急,终于到了最终实例化的时候了:

再次回到ExtensionLoader.getExtension(name)方法

    public T getExtension(String name) {if(name != null && name.length() != 0) {if("true".equals(name)) {return this.getDefaultExtension();} else {//又是各种缓存Holder holder = (Holder)this.cachedInstances.get(name);if(holder == null) {this.cachedInstances.putIfAbsent(name, new Holder());holder = (Holder)this.cachedInstances.get(name);}Object instance = holder.get();if(instance == null) {synchronized(holder) {instance = holder.get();if(instance == null) {//终于找到你了,真正的实例化方法!instance = this.createExtension(name);holder.set(instance);}}}return instance;}} else {throw new IllegalArgumentException("Extension name == null");}}

进入最终的实例化方法createExtension(String name)

    private T createExtension(String name) {//刷新缓存的已知实现类Class clazz = (Class)this.getExtensionClasses().get(name);if(clazz == null) {throw this.findException(name);} else {try {//从已经实例化过的缓存map中获取Object t = EXTENSION_INSTANCES.get(clazz);if(t == null) {//实例化并放入缓存EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());t = EXTENSION_INSTANCES.get(clazz);}//为t进行依赖注入this.injectExtension(t);Set wrapperClasses = this.cachedWrapperClasses;Class wrapperClass;//注入给该t的wrapeprif(wrapperClasses != null && wrapperClasses.size() > 0) {for(Iterator var5 = wrapperClasses.iterator(); var5.hasNext(); t = this.injectExtension(wrapperClass.getConstructor(new Class[]{this.type}).newInstance(new Object[]{t}))) {wrapperClass = (Class)var5.next();}}return t;} catch (Throwable var7) {throw new IllegalStateException("Extension instance(name: " + name + ", class: " + this.type + ")  could not be instantiated: " + var7.getMessage(), var7);}}}

最终返回一个可能进行wrapper包装过的对象!

终于实例化结束,跳出实例化的过程,回到injectExtension()

是不是觉得奇怪,前面在实例化的过程中已经进行了injectExtension()的操作

其实,这里进行注入的对象应该是Protocol$Adaptive,这个动态代理对象!

injectExtension()注入过程

    private T injectExtension(T instance) {try {if(this.objectFactory != null) {Method[] e = instance.getClass().getMethods();int var3 = e.length;for(int var4 = 0; var4 < var3; ++var4) {Method method = e[var4];//啥也别看了,看到set了,可以猜想到通过调用set方法进行注入if(method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {//获取set方法的第一个参数类型Class pt = method.getParameterTypes()[0];try {String e1 = method.getName().length() > 3?method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4):"";//从objectFactory获取扩展类(依赖的某个接口类)//objectFactory前面了解到,是ExtensionFactory的扩展类,用来生产扩展类Object object = this.objectFactory.getExtension(pt, e1);if(object != null) {//注入method.invoke(instance, new Object[]{object});}} catch (Exception var9) {logger.error("fail to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);}}}}} catch (Exception var10) {logger.error(var10.getMessage(), var10);}return instance;}

return instance,回溯到getAdaptiveExtension(),最终getAdaptiveExtension()返回一个由objectFactory生产的实例化且进行依赖注入了的默认扩展类。

稍微整理一下调用链路:

前文中的两个调用链最终生产的对象应该一目了然了:

  1. ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    最终获得的对象为Protocol$Adaptive,且该对象是一个代理对象,最终调用的对象依然是ProtocolFilterWrapper(ProtocolListenerWrapper({extName}Protocol)),extName默认值为SPI注解中的值,这里是dubbo。
  2. ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(“registry”);
    最终获得的对象为ProtocolFilterWrapper(ProtocolListenerWrapper(RegistryProtocol))

那么把前文连接起来,猜想:

     1. ExtensionFactory是用来生产Extension的扩展类2. ExtensionLoader是用来生产实现类的扩展类3. getAdaptiveExtension()是获取该Extension的默认实现类

猜想1.

由ExtensionFactory可以找到com.alibaba.dubbo.common.extension.ExtensionFactory,里面有三种实现spi、spring、adaptive.那么它的调用模式会根据此时获取的extension的类型进行选择性的调用三种实现类的方法。

例如SpiExtensionFactory:

    public class SpiExtensionFactory implements ExtensionFactory {public SpiExtensionFactory() {}public <T> T getExtension(Class<T> type, String name) {//如果该接口标注了@SPIif(type.isInterface() && type.isAnnotationPresent(SPI.class)) {//调用ExtensionLoader.getExtensionLoader(type).getAdaptiveExtension()//获取默认实现类ExtensionLoader loader = ExtensionLoader.getExtensionLoader(type);if(loader.getSupportedExtensions().size() > 0) {return loader.getAdaptiveExtension();}}return null;}}

猜想验证,且应该是ExtensionFactory是用来生产或获取Extension的扩展类,例如从spring容器获取(实例化工作是由spring来完成的)

猜想2.

通过追踪调用链找到createExtension(String name),真正进行了实例化

猜想验证

猜想3.

通过追踪调用链找到实例化对象为Protocol$Adaptive(以Protocol为例)

发现在方法调用过程中有这样的一句话:

    //SPI中配置项为"dubbo",所以这里编译出来的默认值为dubboString extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());

且该方法上标注了@Adaptive的注解

可见,@SPI中的注解表示该接口的默认实现类而在方法上标注@Adaptive注解,则是在该方法上增加可选项,允许通过传入不同的extName(这个例子是通过getProtocol的方式获得此时应该适应的对象)来调用自适应对象的方法。

通俗的理解就是,

当接口类上标注了@SPI注解,说明了该接口类的默认实现类

当接口类上标注了@SPI注解,且方法上标注了@Adaptive,则说明了该接口类的该方法需要根据情况调用不同的实现类

当接口类上标注了@Adaptive,则说明是根据需要直接实例化某实现类

详情可以查阅@SPI和@Adaptive注解说明。

虽然猜想3不能完成验证,但至少通过跟踪分析了SPI的调用链路让我们有比较充分的理由这样推测。

Dubbo系列(二)源码分析之SPI机制相关推荐

  1. Java集合Collection源码系列-ArrayList源码分析

    Java集合系列-ArrayList源码分析 文章目录 Java集合系列-ArrayList源码分析 前言 一.为什么想去分析ArrayList源码? 二.源码分析 1.宏观上分析List 2.方法汇 ...

  2. Dubbo 服务订阅源码分析

    Dubbo 服务引用的时机有两个: 第一个是在 Spring 容器调用 ReferenceBean 的 afterPropertiesSet 方法时引用服务 第二个是在 ReferenceBean 对 ...

  3. Memcached源码分析 - 内存存储机制Slabs(5)

    Memcached源码分析 - 网络模型(1) Memcached源码分析 - 命令解析(2) Memcached源码分析 - 数据存储(3) Memcached源码分析 - 增删改查操作(4) Me ...

  4. dubbo的Extension源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 我们基于ExtensionLoader.getExtensionLoader().getAdaptiveExtension() ...

  5. Linux kernel SPI源码分析之SPI设备驱动源码分析(linux kernel 5.18)

    SPI基础支持此处不再赘述,直接分析linux中的SPI驱动源码. 1.SPI设备驱动架构图 2.源码分析 本次分析基于kernel5.18,linux/drivers/spi/spidev.c 设备 ...

  6. Dubbo服务注册源码分析

    本代码版本基于Dubbo2.7.8版本进行源码分析 注册概览 扫描所有@DubboService注解, 加载配置文件, 装载注解中的所有属性, 把每个服务都封装成一个ServiceBean, 注入到S ...

  7. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  8. 鸿蒙内核源码分析:调度机制篇

    作者 | 深入研究鸿蒙,鸿蒙内核发烧友 出品 | CSDN(ID:CSDNnews) 头图 | CSDN 下载自东方 IC 阅读之前建议先读本系列其他文章,以便对本文任务调度机制的理解. 为什么要学这 ...

  9. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发机制...

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

最新文章

  1. 中国半光纸市场供需形势分析及运行环境研究报告2021年版
  2. docker电子书_果然!这10个Docker容器坑多数人都中招过
  3. Python之深入解析如何使用Python Kivy实现一个“乒乓球”游戏
  4. (七)数据结构之“字典”
  5. csdn无人驾驶汽车_无人驾驶汽车100年历史
  6. 乐视android版本点四下,EUI5.9+Android7.0刷机包
  7. 485串口光纤转换器产品介绍
  8. struts2 poi excel 导出
  9. python读取行政区txt文件实现行政区树_数据库的点数据根据行政区shp来进行行政区处理,python定时器实现...
  10. 继承(1)----《.NET 2.0面向对象编程揭秘 》学习
  11. Qt QScrollArea and layout in code
  12. java模板导出excel_POI导出excel模板三种方式
  13. 交互式绘图软件源码开发,简单好用功能强大的绘图工具
  14. 如何用计算机名添加的打印机,如何添加打印机,小编教你添加共享打印机的方法...
  15. 代码重构(一)原理和规范
  16. 静态页面笔记包括 html和css
  17. 2021-07-18 游戏仓、力反馈
  18. 最大似然估计log likelihood
  19. GCC and MinGW-w64 for Windows
  20. 笔记本连接html后分成两个屏,一台电脑两个显示器是如何来实现 一台电脑两个显示器连接方法...

热门文章

  1. 200821-阿里云服务器ECS学生使用经验小节(非CS折腾了大半天)
  2. 什么是IC封测?语音芯片封装与测试的流程步骤
  3. Python——函数(2)之常见函数
  4. Cadence OrCAD 原理图栅格样式的大小设置方法图文教程及视频演示
  5. Pipeline流水线项目构建
  6. 小程序-小程序审核时间
  7. Quartus生成原理图
  8. 留言薄+留言薄==BBS
  9. react 中的闭包陷阱
  10. Ubuntu网络配置(NAT模式)