前言

上一篇简单的介绍了spi的基本一些概念,在末尾也提到了,dubbo对jdk的spi进行了一些改进,具体改进了什么,来看看文档的描述

JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。

如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。

增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

根据小学语文的阅读理解,不难概括出其实就是提高了性能和增加了功能.很多朋友都喜欢问,阅读源码不如从何下手,要准备些什么.如果只是粗略阅读源码,掌握大体思路,其实具备小学语文的阅读理解和看图写作业就差不多了(能看到本篇的均完全胜任这个条件,所以不要有任何恐惧心理).但是要领悟思想,对细节了如指掌,甚至写出更优秀的框架,那么就是四个字->终身学习.(比如现在关注肥朝,每周一篇,一起交流讨论,终身学习即刻开启)

对于阅读源码,当然还是要有一些小技巧,俗话说得号,"技多不压身",我们先看一个段子

某肥遇到了一个bug,需要引入多线程,结果引入多线程后,竟然出现了两个bug

由此可见,多线程也确实是大家头疼的问题,所以后面我也会演示一些小技巧,比如

  • 如何调试多线程代码(手把手实战,不讲理论)
  • 如何查看代理对象源码(手把手实战,不讲理论)

那dubbo这个改良后的spi究竟怎么提高性能,又增加了什么功能,那就是本篇要讲的.

插播面试题
既然你对spi有一定了解,那么dubbo的spi和jdk的spi有区别吗?有的话,究竟有什么区别?
概念铺垫
dubbo的拓展点机制涉及到众多的知识点,也是dubbo中比较难的地方,和之前的集群容错有Cluster、Directory、Router、LoadBalance关键词一样,这个拓展点机制也有几个关键词,SPI、Adaptive、Activate.这些会陆续讲解,最后总结.

直入主题
提升性能
提升性能,我们最容易想到的方式是什么?其实这个和初高中政治答题一样,有万能公式的,那就是"缓存".所以面试无论问你什么(适用于Android,iOS,Web前端,Java等等...),只要和提升性能有关的,往缓存方向答肯定没错(当然按照"按点给分"的套路,往缓存方向答只是不至于让你拿0分,但是仅仅答缓存肯定拿不到满分).所以如果与jdk的spi对比,那么可以有以下几个点

1.从"万能公式"角度分析,增加缓存

因为部分朋友反馈说喜欢贴代码的形式,所以讲解在注释中

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {if (type == null)//拓展点类型非空判断throw new IllegalArgumentException("Extension type == null");if(!type.isInterface()) {//拓展点类型只能是接口throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");}if(!withExtensionAnnotation(type)) {//需要添加spi注解,否则抛异常throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");}//从缓存EXTENSION_LOADERS中获取,如果不存在则新建后加入缓存//对于每一个拓展,都会有且只有一个ExtensionLoader与其对应ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);if (loader == null) {EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);}return loader;
}private ExtensionLoader(Class<?> type) {this.type = type;//这里会存在递归调用,ExtensionFactory的objectFactory为null,其他的均为AdaptiveExtensionFactory//AdaptiveExtensionFactory的factories中有SpiExtensionFactory,SpringExtensionFactory//getAdaptiveExtension()这个是获取一个拓展装饰类对象.细节在下篇讲解objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

以下是缓存拓展点对象的

public T getExtension(String name) {if (name == null || name.length() == 0)throw new IllegalArgumentException("Extension name == null");if ("true".equals(name)) {return getDefaultExtension();}Holder<Object> holder = cachedInstances.get(name);if (holder == null) {cachedInstances.putIfAbsent(name, new Holder<Object>());holder = cachedInstances.get(name);}Object instance = holder.get();if (instance == null) {synchronized (holder) {instance = holder.get();if (instance == null) {instance = createExtension(name);holder.set(instance);}}}return (T) instance;
}

为什么这个要单独拿出来说呢?很多朋友容易产生大意心理,以为缓存嘛,无非就是判断一下是否存在,不存在则添加.dubbo也不过如此.我不看源码也懂.但是你如果稍加注意,就会发现它在线程安全细节方面做得很好,比如

private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();

现在知道为什么面试问完HashMap喜欢问ConcurrentHashMap了吧?这还不是个关键,我们继续看还有个很重要的类Holder,这个类用于保存一个值,并且给值添加volatile来保证线程的可见性.

public class Holder<T> {private volatile T value;public void set(T value) {this.value = value;}public T get() {return value;}}

看源码更重要的是看到这些细节,魔鬼都藏在细节当中!

2.从注解角度入手分析

既然是对比spi区别,并且dubbo中有@spi这个注解,那我们顺着注解看看能有什么线索.

如果在15年有用过dubbo,那么就会留意到@Extension这个注解,但是后来因为含义广泛废弃,换成了@SPI.

@SPI("javassist")
public interface Compiler {//省略...
}

public Class<?> compile(String code, ClassLoader classLoader) {Compiler compiler;ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);String name = DEFAULT_COMPILER; // copy referenceif (name != null && name.length() > 0) {compiler = loader.getExtension(name);} else {compiler = loader.getDefaultExtension();}return compiler.compile(code, classLoader);
}

//com.alibaba.dubbo.common.compiler.Compiler 文件配置如下
adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

我们从上面这两部分代码和配置文件就不难分析出两点(如果对spi不熟悉的请先把上一篇spi(一)看一遍,基础不牢地动山摇的情况下没法分析)

JDK的spi要用for循环,然后if判断才能获取到指定的spi对象,dubbo用指定的key就可以获取

//返回指定名字的扩展
public T getExtension(String name){}

JDK的spi不支持默认值,dubbo增加了默认值的设计

//@SPI("javassist")代表默认的spi对象,比如Compiler默认使用的是javassist,可通过
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
compiler = loader.getDefaultExtension();
//方式获取实现类,根据配置,即为
//com.alibaba.dubbo.common.compiler.support.JavassistCompiler

增加功能

增加的功能,就如文档所说的,spi增加了IoC、AOP

AOP这是个老生常谈的话题了,谈到AOP大家最容易联想到Spring,甚至因为AOP常用在事务的场景,甚至就有不少人认为AOP就是事务.所以肥朝建议初学者学习AOP的路线大致如下:

// 这一步步演进的过程,才是最大的收获
装饰者设计模式->静态代理->JDK、CGLIB动态代理->AOP

这里说的dubbo增加的IoC和AOP因为涉及到一个重要注解@Adaptive,后面会有专门一篇来详细讲,敬请期待.

写在最后

本篇以spi为引线,主要想表达的是分析问题的思路,新技术虽然层出不穷,但是只要掌握原理和分析问题的思路,自然能应对千变万化.鉴于肥朝才疏学浅,分析问题的思路仍有很多缺陷和不足,不足之处还望你不吝斧正.期待下周与你相遇,

Ref:

https://www.jianshu.com/p/e7446cdc7161

转载于:https://www.cnblogs.com/winner-0715/p/8726902.html

dubbo源码解析-spi(二)相关推荐

  1. dubbo源码解析(二)

    大家好,我是烤鸭: dubbo 源码解析: 1.服务导出 介绍: Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三 ...

  2. Dubbo源码解析-——SPI机制

    文章目录 一.什么是SPI机制 二.Java原生的SPI机制 2.1.javaSPI示例 2.1.1.编写接口和实现类 2.1.2.编写配置文件 2.1.3.通过SPI机制加载实现类 2.1.4.JA ...

  3. dubbo源码解析-SPI机制

    SPI,Service Provider Interface,服务提供者接口,是一种服务发现机制. JDK 的 SPI 规范 JDK 的 SPI 规范规定:  接口名:可随意定义  实现类名:可随 ...

  4. dubbo源码解析(十)远程通信——Exchange层

    远程通讯--Exchange层 目标:介绍Exchange层的相关设计和逻辑.介绍dubbo-remoting-api中的exchange包内的源码解析. 前言 上一篇文章我讲的是dubbo框架设计中 ...

  5. 【dubbo源码解析】 --- dubbo spi 机制(@SPI、@Adaptive)详解

    本文对应源码地址:https://github.com/nieandsun/dubbo-study 注意:dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外 文章目 ...

  6. dubbo源码解析(九)远程通信——Transport层

    远程通讯--Transport层 目标:介绍Transport层的相关设计和逻辑.介绍dubbo-remoting-api中的transport包内的源码解析. 前言 先预警一下,该文篇幅会很长,做好 ...

  7. dubbo(5) Dubbo源码解析之服务调用过程

    来源:https://juejin.im/post/5ca4a1286fb9a05e731fc042 Dubbo源码解析之服务调用过程 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与 ...

  8. dubbo源码解析之框架粗谈

    dubbo框架设计 一.dubbo框架整体设计 二.各层说明 三.dubbo工程模块分包 四.依赖关系 五.调用链 文章系列 [一.dubbo源码解析之框架粗谈] [二.dubbo源码解析之dubbo ...

  9. Dubbo源码解析 —— Router

    作者:肥朝 原文地址:http://www.jianshu.com/p/278e782eef85 友情提示:欢迎关注公众号[芋道源码].????关注后,拉你进[源码圈]微信群和[肥朝]搞基嗨皮. 友情 ...

最新文章

  1. OpenGL: 3D坐标到屏幕坐标的转换逻辑(gluProject的实现)(转)
  2. centos mongodb安装及简单实例
  3. 2020 我的C++学习之路 C++PrimerPlus第七章课后习题
  4. Spring Cloud + Spring Boot + Mybatis + shiro + RestFul + 微服务
  5. linux fork, system, exec()
  6. 二叉树的最近公共祖先—leetcode236
  7. centos7安装python-pip
  8. Dubbo下一站:Apache顶级项目
  9. 华为官方推特直接开骂@苹果,负责人称:被盗号了
  10. 企业会计准则2020版pdf_2020年下半年CATTI三级笔译中译英真题+参考答案+原文件汉英对照PDF版...
  11. git add remote_git命令
  12. python 操作excel神器_【转】多图+代码 | 详解Python操作Excel神器openpyxl的各种操作!...
  13. Python 实现多元线性回归预测
  14. 阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第6节 Lambda表达式_5_Lambda表达式的无参数无返回值的...
  15. Java学习笔记——Lambda
  16. 大数据开发入门怎么学习?
  17. 谷歌浏览器正式版(稳定版)离线安装包下载大全
  18. uniapp 返回页面刷新/小程序获取用户信息之后登录和退出登录
  19. python怎么表达我喜欢你的句子_20句暗示“我喜欢你”的告白句子,句句触动人心...
  20. 三菱FX5U系列程序 伺服轴自动运行计数回原点程序,工位单步运行程序,轴JOG,回原点,绝对定位,相对定位,力矩控制

热门文章

  1. MySQL -- 行转列 -- GROUP_CONCAT -- MAX(CASE WHEN THEN)
  2. 【转】聊聊HTTPS和SSL/TLS协议
  3. 开玩笑html5(五岁以下儿童)---绕地球月球,地球绕太阳运动(canvas实现,同样可以移动哦)...
  4. HIT Software Construction Review Notes(2-1 Software Lifecycle and Configuration Management)
  5. 解决VScode安装Babel转码器报错:无法将“cnpm“项识别为cmdlet\函数、脚本文件或可运行程序的名称的问题
  6. excel怎么批量插行_批量制作anki卡片最易上手方法
  7. 逻辑删除用户后 用户名重复怎么办?
  8. 越来越多优秀的老员工辞职,怎么办?
  9. 在更新iOS14.5後,App要追踪我們會先收到彈窗提示
  10. 为什么现在辞职的年轻教师越来越多?