前言

上一篇简单的介绍了spi的基本一些概念,但是其实Dubbo对jdk的spi进行了一些改进,具体改进了什么,来看看文档的描述

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

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

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

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

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

插播面试题

  • 既然你对spi有一定了解,那么Dubbo的spi和jdk的spi有区别吗?有的话,究竟有什么区别?

  • 你说你看过Dubbo源码,那你简单说下,他在设计上有哪些细节让你觉得很巧妙?(区分度高)

概念铺垫

Dubbo的拓展点机制涉及到众多的知识点,也是Dubbo中比较难的地方,和之前的集群容错有ClusterDirectoryRouterLoadBalance关键词一样,这个拓展点机制也有几个关键词,SPIAdaptiveActivate.这些会陆续讲解,最后总结.

直入主题

提升性能

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

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

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

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

  

以下是缓存拓展点对象的

 1  public T getExtension(String name) {
2 if (name == null || name.length() == 0)
3 throw new IllegalArgumentException("Extension name == null");
4 if ("true".equals(name)) {
5 return getDefaultExtension();
6 }
7 Holder<Object> holder = cachedInstances.get(name);
8 if (holder == null) {
9 cachedInstances.putIfAbsent(name, new Holder<Object>());
10 holder = cachedInstances.get(name);
11 }
12 Object instance = holder.get();
13 if (instance == null) {
14 synchronized (holder) {
15 instance = holder.get();
16 if (instance == null) {
17 instance = createExtension(name);
18 holder.set(instance);
19 }
20 }
21 }
22 return (T) instance;
23}

  

 

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

敲黑板划重点

在上一篇从Dubbo内核聊聊双亲委派机制中我就强调了,无论做什么,都要形成差异性.在Java中,最容易形成差异性的知识点,就是JVM并发包.既然上一篇中我们提了JVM相关的内容(双亲委派机制),那么本篇我们就说说并发包的相关内容.我们仔细看上面的这段double-checked locking代码

 1  //double-checked locking
2 Object instance = holder.get();
3 if (instance == null) {
4 synchronized (holder) {
5 instance = holder.get();
6 if (instance == null) {
7 instance = createExtension(name);
8 holder.set(instance);
9 }
10 }
11 }
1 public class Holder<T> {
2
3 private volatile T value;
4
5 public void set(T value) {
6 this.value = value;
7 }
8
9 public T get() {
10 return value;
11 }
12
13}

  

这里为什么用到了volatile关键字呢?看源码更重要的是看到这些细节,魔鬼都藏在细节当中!本来肥朝想展开讲一下这个volatile秀一波操作的,但是无奈篇幅有限,那个我给你个关键词.doubleCheck Singleton 重排序,你把这个关键词对着浏览器一搜.然后把搜索到的第一页结果都看完,你就知道这段代码并不是你想的只是判空加个缓存这么简单.他在并发细节上是有考虑的

2.从注解角度入手分析

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

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

1  @SPI("javassist")
2 public interface Compiler {
3 //省略...
4 }
1 public Class<?> compile(String code, ClassLoader classLoader) {
2 Compiler compiler;
3 ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
4 String name = DEFAULT_COMPILER; // copy reference
5 if (name != null && name.length() > 0) {
6 compiler = loader.getExtension(name);
7 } else {
8 compiler = loader.getDefaultExtension();
9 }
10 return compiler.compile(code, classLoader);
11 }
1 //com.alibaba.dubbo.common.compiler.Compiler 文件配置如下
2 adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
3 jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
4 javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler

  

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

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

1//返回指定名字的扩展
2public T getExtension(String name){}
 
  • JDK的spi不支持默认值,dubbo增加了默认值的设计

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

  

 

增加功能

增加的功能,就如文档所说的,spi增加了IoCAOP

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

原文:https://mp.weixin.qq.com/s/HQb4MpiqJBrqi2u7Zscnrw

【转】Dubbo和JDK的SPI究竟有何区别?的更多相关文章

  1. dubbo源码解析-spi(二)

    前言 上一篇简单的介绍了spi的基本一些概念,在末尾也提到了,dubbo对jdk的spi进行了一些改进,具体改进了什么,来看看文档的描述 JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩 ...

  2. Dubbo 源码分析 - SPI 机制

    1.简介 SPI 全称为 Service Provider Interface,是 Java 提供的一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加 ...

  3. Dubbo原理解析-Dubbo内核实现之SPI简单介绍

    转自:https://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 Dubbo 采用微内核+插件体系,使得设计优雅,扩展性强.那所谓 ...

  4. 1. Dubbo原理解析-Dubbo内核实现之SPI简单介绍 (转)

    转载自  斩秋的专栏  http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 Dubbo采用 微内核 + 插件体系,使得设计优 ...

  5. dubbo源码解析-spi(3)

    前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...

  6. dubbo源码解析-spi(一)

    前言 虽然标题是dubbo源码解析,但是本篇并不会出现dubbo的源码,本篇和之前的dubbo源码解析-简单原理.与spring融合一样,为dubbo源码解析专题的知识预热篇. 插播面试题 你是否了解 ...

  7. Dubbo内核实现之SPI简单介绍

    这个部分单独写一页,看起来更高大上一些. 1.概括 Dubbo采用微内核+插件体系,使得设计优雅,扩展性强.那所谓的微内核+插件体系是如何实现的呢! 即我们定义了服务接口标准,让厂商去实现(如果不了解 ...

  8. 【DUBBO】Dubbo原理解析-Dubbo内核实现之SPI简单介绍

    Dubbo采用微内核+ 插件体系,使得设计优雅,扩展性强.那所谓的微内核+插件体系是如何实现的呢!大家是否熟悉spi(service providerinterface)机制,即我们定义了服务接口标准 ...

  9. dubbo学习之路-SPI机制

    dubbo学习之路-SPI机制 1.SPI 1.1Java SPI 原理 SPI是service provider interface简称.在java JDK中 内置的一种服务提供发现机制.它解决在一 ...

随机推荐

  1. jquery print属性设置

    一,需求背景 项目中遇到打印功能,想实现完美的打印功能,对元素进行操作等,可以使用jquery print 插件. 二,依赖文件  git地址请戳这里 1,jquery 2,jquery.print- ...

  2. Django基础—1

    一. Django的安装1. 查看已安装的Django的版本     进入到终端以及Python的交互模式    python3/ ipython32. 交互模式中输入import django    ...

  3. 关于esp32的系统初始化启动过程及设计学习方法

    对于esp32,其开发程序中有且只能有一个app_main函数,该函数是用户程序的入口,这在没有调用FreeRTOS的系统中相当于函数main,但其实在app_main之前,系统还有一段初始化的过程, ...

  4. jvm虚拟机--垃圾回收子系统

    转载自cyc2018的github:https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Java%20%E8%99%9A%E ...

  5. [Project] MiniSearch文本检索简介

    1. 预处理过程 预处理主要用来事先生成程序在运行过程中可能用到的数据,以便加速处理时间. 预处理的过程主要生成程序所需的三个文件:网页库文件,网页位置信息文件和倒排索引文件. 网页库文件 其中网页库 ...

  6. 使用TheFolderSpy监控文件夹的变化-邮件通知

    一.概述 当我们的文档或者代码文件发布在公网.共享文件夹中,其他用户具备访问或修改的权限时,就存在文档被覆盖或删除的分享.另外一个典型的场景,发布在Web服务器上的网页文件,在网站版本不更新的时间,服 ...

  7. Vue学习笔记八:v-for,v-if,v-show指令

    目录 v-for指令:遍历 HTML和效果图 v-for讲解 v-if和v-show:创建,删除,显示,隐藏 HTML和效果图 v-if和v-show的原理 v-for指令:遍历 HTML和效果图 有 ...

  8. Avoiding Common Networking Mistakes

    https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/Networkin ...

  9. MySQL:基础架构和工作流程

    [参考文章]:01|基础架构:一条查询语句的执行流程 1. 基本架构 大体来说,MySQL可以分为Server层和存储引擎两部分. Server层包括链接器,分析器,优化器,执行器等,涵盖大多数核心服 ...

  10. js学习笔记----JavaScript中DOM扩展的那些事

    什么都不说,先上总结的图~   Selectors API(选择符API) querySelector()方法 接收一个css选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null ...