前言

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

插播面试题

  • 你是否了解spi,讲一讲什么是spi,为什么要使用spi?

  • 对类加载机制了解吗,说一下什么是双亲委托模式,他有什么弊端,这个弊端有没有什么我们熟悉的案例,解决这个弊端的原理又是怎么样的?

spi的简单介绍

如果提到api相信大家都知道,spi的话,知道的人就相对少一些。

简单的说,api是给使用者使用的,spi是给拓展者使用的.一个好的开源框架,必须要留一些拓展点.让参与者尽量黑盒拓展,而不是白盒修改代码,否则分支,质量,合并,冲突都会很难管理.并且框架作者能做到的功能,拓展者也一定能做到.

如果从使用层面来说,就是运行时,动态给接口添加实现类.其实这有点像IoC的思想,将装配的控制权移到程序之外

如果从生活中的例子讲,就是比如浏览器插件,比如墙上的插头不够我们就接个排插,而不是伤筋动骨改插头(感觉不是很贴切,前期你暂且这么不规范的粗略理解)

再多的言语都是抽象的,那么我们用代码来简单实现一下spi

spi的简单实现

接口和具体实现类

public interface ISayName {
void say();
}

接口的一种实现

public class SayEnglishName implements ISayName{
@Override
public void say() {
System.out.println("Toby");
}
}

接口的另一实现

public class SayChineseName implements ISayName{
@Override
public void say() {
System.out.println("肥朝");
}
}

配置文件,需放置在META-INF/services/接口全限定名

com.toby.spi.impl.SayChineseName
com.toby.spi.impl.SayEnglishName

demo目录结构

测试结果如下

通过改变配置文件,我们就能动态的改变一个接口的实现类.

细心的小伙伴可能发现,比如我想新增一个实现类SayFranceNameImpl,这样的话光改配置文件也还是不行,还要预先包里面就有这个实现类才行啊.

这个先别急,后面会介绍javassist,也就是动态字节码技术.这样可以在运行时动态生成Java类,就不存在要预先把接口的实现类先在包里放好.更多内容,关注肥朝即可.

当然细心的小伙伴可能还发现了,这个我就算不用spi,我用spring的ioc也能通过配置文件动态的注入不同的实现类啊

比如dubbo的设计中,就不想强依赖Spring的IoC容器,但是自已造一个小的IoC容器,也觉得有点过度设计.另外dubbo是不需要依赖任何第三方库的,引用官方文档原话如下

理论上 Dubbo 可以只依赖 JDK,不依赖于任何三方库运行,只需配置使用 JDK 相关实现策略

敲黑板划重点

经常看到有人问两类问题

  • java人这么多,是否饱和了?
  • 为什么总是要面试造火箭,进去拧螺丝?

你可以问一下你同事,你知道什么是spi吗,如果他不知道的话,那你觉得他把上面的这个简单的例子实现要多久?如果从使用这个层面做区分的话,很难做到有效的区分.无论是做什么,要想在竞争中脱颖而出,就必须做到三个字.差异化.比如之前买喜茶要排很长的队,这其实也是一种差异化.

Java基础中比较容易产生差异化的两个区域就在于JVM和并发编程.如果只是停留在使用层面,那么关注肥朝的博客意义并不大,因此,本篇的spi还需要与ClassLoader结合.

学习JVM和并发编程买本书是必不可少的,以下内容参考了实战Java虚拟机.如果你看的是深入Java虚拟机也没关系,不要纠结于获取知识的渠道,没人在意你做的是五年高考三年模拟还是王后雄学案.
以下内容截取了该书中的部分核心内容,非常感谢作者的辛勤奉献(希望大家支持正版书籍).

从ClassLoader引出spi

ClassLoader的简单介绍

Class的装载大体上可以分为加载类、连接类和初始化三个阶段,在这三个阶段中,所有的Class都是由ClassLoader进行加载的,然后Java虚拟机负责连接、初始化等操作.也就是说,无法通过ClassLoader去改变类的连接和初始化行为.

Java虚拟机会创建三类ClassLoader,分别是

  • BootStrap ClassLoader(启动类加载器)
  • Extension ClassLoader(扩展类加载器)
  • APP ClassLoader(应用类加载器,也称为系统类加载器)

此外,每个应用还可以自定义ClassLoader

ClassLoader的双亲委托模式

在ClassLoader的结构中,还有一个重要的字段parent,它也是一个ClassLoader的实例,这个字段表示的ClassLoader也称为这个ClassLoader的双亲.在类加载的过程中,可能会将某些加载类的请求交于自己的双亲处理.

如图,应用类加载器的双亲为扩展类加载器,扩展类加载器的双亲为启动类加载器.

系统中的ClassLoader在协同工作时,默认会使用双亲委托模式.即在类加载的时候,系统会判断当前类是否已经被加载,如果被加载,就会直接返回可用的类,否则就会尝试加载,在尝试加载时,会先请求双亲处理,如果双亲请求失败,则会自己加载.

双亲委托模式的弊端

判断类是否加载的时候,应用类加载器会顺着双亲路径往上判断,直到启动类加载器.但是启动类加载器不会往下询问,这个委托路线是单向的,即顶层的类加载器,无法访问底层的类加载器所加载的类,如图

 

启动类加载器中的类为系统的核心类,比如,在系统类中,提供了一个接口,并且该接口还提供了一个工厂方法用于创建该接口的实例,但是该接口的实现类在应用层中,接口和工厂方法在启动类加载器中,就会出现工厂方法无法创建由应用类加载器加载的应用实例问题.

拥有这样问题的组件有很多,比如JDBC、Xml parser等.JDBC本身是java连接数据库的一个标准,是进行数据库连接的抽象层,由java编写的一组类和接口组成,接口的实现由各个数据库厂商来完成

双亲委托模式的补充

在Java中,把核心类(rt.jar)中提供外部服务,可由应用层自行实现的接口,这种方式称为为spi.那我们看一下,在启动类加载器中,访问由应用类加载器实现spi接口的原理

Thread类中有两个方法

public ClassLoader getContextClassLoader()//获取线程中的上下文加载器
public void setContextClassLoader(ClassLoader cl)//设置线程中的上下文加载器

通过这两个方法,可以把一个ClassLoader置于一个线程的实例之中,使该ClassLoader成为一个相对共享的实例.这样即使是启动类加载器中的代码也可以通过这种方式访问应用类加载器中的类了.如下图

 

写在最后

本篇为2017年的最后一篇,提前祝大家元旦快乐.2018年,每周一篇dubbo源码解析还会继续更新.原因很简单,越是忙碌才越要抽出时间学习.我们要训练的是如何打逆风局,而不是怎么打顺风局.当然我非常不提倡大家像我一样熬夜(因明天要外出,所以不得不在今晚完成),不要忘了目前很火的电视剧虎啸龙吟中司马懿是怎么把诸葛亮熬死的.

有了本篇的知识铺垫,下一篇将从dubbo源码入手,看看dubbo是如何实现spi的.另外剧透一下,dubbo还对JDK标准的spi进行了一些改进.期待下周继续与你相遇.鉴于本人才疏学浅,不对的地方还望斧正。

Ref:

dubbo源码解析-spi(一)的更多相关文章

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

    前言 本篇是spi的第四篇,本篇讲解的是spi中增加的AOP,还是和上一篇一样,我们先从大家熟悉的spring引出AOP. AOP是老生常谈的话题了,思想都不会是一蹴而就的.比如架构设计从All in ...

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

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

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

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

  4. Dubbo源码(二) - SPI源码

    前情提要 假设你已经知道Dubbo SPI的使用方式,不知道的请出门左转: Dubbo源码(一) - SPI使用 Dubbo源码地址: apache/dubbo 本文使用版本:2.6.x 测试Demo ...

  5. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  6. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  7. dubbo源码解析-zookeeper创建节点

    前言 在之前dubbo源码解析-本地暴露中的前言部分提到了两道高频的面试题,其中一道dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能通信吗?在上周的dubbo源码 ...

  8. Dubbo源码解析之SPI(一):扩展类的加载过程

    Dubbo是一款开源的.高性能且轻量级的Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用.智能容错和负载均衡,以及服务自动注册和发现. Dubbo最早是阿里公司内部的RPC框架,于 ...

  9. 【Dubbo 源码解析】02_Dubbo SPI

    Dubbo SPI:(version:2.6.*) Dubbo 微内核 + 插件 模式,得益于 Dubbo SPI .其中 ExtentionLoader是 Dubbo SPI 最核心的类,它负责扩展 ...

随机推荐

  1. Spring拦截器和过滤器

    什么是拦截器 拦截器(Interceptor): 用于在某个方法被访问之前进行拦截,然后在方法执行之前或之后加入某些操作,其实就是AOP的一种实现策略.它通过动态拦截Action调用的对象,允许开发者 ...

  2. Lighthouse前端性能优化测试工具

    在前端开发中,对于自己开发的app或者web page性能的好坏,一直是让前端开发很在意的话题.我们需要专业的网站测试工具,让我们知道自己的网页还有哪些需要更为优化的方面,我自己尝试了一款工具:Lig ...

  3. django邮件

    1.邮件变量 (django settings.py) ADMINS = [('JOHN','JOHN@example.com'),('zhang','zhang@example.com')] #设置 ...

  4. java中关于锁知识的整理

    1.1什么是锁? 在计算机科学中,锁(lock)或互斥(mutex)是一种同步机制,用于在有许多执行线程的环境中强制对资源的访问限制.锁旨在强制实施互斥排他.并发控制策略. 锁通常需要硬件支持才能有效 ...

  5. react初始化阶段

    初始化阶段可以使用的函数:getDefaultProps:只调用一次,实例之间共享引用.只有在组件的第一个实例被初始化的时候,才会调用他,然后react会把这个函数的返回结果保存起来,从第二个实例开始 ...

  6. Outdated Kotlin Runtime

    你的 kotlin 运行时版本 在 1.1.2库中 是 1.1.2 然而插件版本是 1.1.4 . 运行时库 应该被更新,避免兼容问题. Outdated Kotlin Runtime Your ve ...

  7. ant design的一些坑

    1.在本地修改ant design的某些样式可以生效,但在线上就失效了.比如collapse组件里的箭头图标在本地和在线上的类名有变化,本地类名,线上类名:箭头图标的svg样式在线上会自动添加一个内联 ...

  8. CentOS 7搭建KVM在线管理面板WebVirtMgr

    系统版本:CentOS 7.4 WebVirtMgr版本:master分支的20180720版本,下载链接(链接:https://pan.baidu.com/s/1kl060hPHDGbwJUR_iM ...

  9. Redis "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk"问题的解决(转)

    今天第二次遇到Redis “MISCONF Redis is configured to save RDB snapshots, but is currently not able to persis ...

  10. VGA Signal Timing

    VGA Signal Timing 640 x 350 VGA 640x350@70 Hz (pixel clock 25.175 MHz) VESA 640x350@85 Hz (pixel clo ...