文档章节

Dubbo源码分析-微内核插件式开发(SPI介绍)

徐安是个好人
 徐安是个好人
发布于 2017/04/28 20:55
字数 1412
阅读 81
收藏 1

1、前言

阅读优秀的开源框架是程序员进步的一种捷径。希望自己能坚持下去。有个好头也有个好的结尾。这部分先讲讲dubbo最核心的设计思想。微内核插件式开发。这种思想贯穿了他的整个项目。在讲微内核之前,可能先要讲讲java的SPI。其实dubbo就是在SPI设计思想基础上进行了升级。

2、什么是SPI

SPI全称Service Provider Interface。字面上理解是面向接口提供服务。他很好的诠释了面向接口编程,以及OCP原则等。大家都知道,我们在模块化编程的时候,往往模块之间是基于接口编程的。例如A模块需要使用一种服务,但具体实现交由第三方实现。那么A会定义出一个接口让B去实现,但是A在实例化这个接口的实现时,往往需要硬编码B的一种实现。这就会导致B如果换了实现方式,又或者A想使用C的实现方式,那么需要修改A的实例化代码去支持实现的修改切换。在OCP原则里,这种修改是需要避免的。所以我们需要一种实现发现机制。在实例化时不应该硬编码一种实现,而是为接口提供一种实现发现机制,然后把实例化这部分功能移到A模块之外。这种思想跟IOC很像。我们在用Spring的时候,就是把对象的装配交给了Spring处理,这样层于层之间才正真达到了面向接口编程。

3、SPI实现思想

假如A模块设计定义了一个接口。当B实现了这个接口之后,需要在B的jar包的META-INF/services/目录里添加一个文件,这个文件名字就是A定义的这个接口的全路径名,内容就是B实现接口的类的全路径名。此时,如果A需要使用B的实现,引入B的jar包。然后A可以使用java.util.ServiceLoader去发现这个实现,并实例化后返回给A。这样A在使用这个实现时,只是用定义好的接口引用了该实现,不用去硬编码这个实现。如果有一天需要替换B的实现,我们只要引入别的第三方jar,根本不用修改A的代码。

4、看例子说话

例如A模块定义了一个接口,如下:

public interface DemoApi {
    String sayHello(String name);
}

然后B实现这个接口,如下:

public class DemoApiImpl1 implements DemoApi {
    public String sayHello(String name) {
        return name + "你好,我是DemoApiImpl1实现";
    }
}

假如我们不用SPI设计思想去设计程序,那么我在A模块中要需要下面这样去使用DemoApi接口,这个问题就来了,我在设计A模块的时候,需要去依赖B模块,而且哪天我想替换B模块的实现,我还得动A模块的代码,重新new一个别的实现。其实A模块只需要使用DemoApi接口的服务,至于这个接口的具体实现根本不需要关心,更不需要事先依赖某种实现。

public static void main(String args[]) {
    DemoApi demoApi = new DemoApiImpl1();
    demoApi.sayHello("xuanner");
}

那么,我们来改进一下,使用SPI来编码使用DemoApi接口的服务,如下:

public static void main(String args[]) {
    ServiceLoader<DemoApi> serviceLoader = ServiceLoader.load(DemoApi.class);
    Iterator<DemoApi> iterator = serviceLoader.iterator();
    System.out.println("可以遍历多种可能存在的实现");
    while (iterator.hasNext()) {
        DemoApi demoApi = iterator.next();
        demoApi.sayHello("xuanner")
    }
}

当然,B模块在实现之后需要在他的JAR包的META-INF/services/目录下放一个配置文件,看如下图:

里面的内容是实现类的全路径,如下:

com.xuan.spidemo.impl1.DemoApiImpl1

当A模块需要B的实现的时候,只要进入B的JAR,ServiceLoader就会在JAR下找到对应接口的实现,然后实例化返回提供给A使用,如果有一天我们需要替换用C去实现,那么只要C实现代码后,同样在他的JAR下放上面的配置文件即可。

5、举例现有的优秀实现

其中java.sql.Driver的实现就就是基于使用了SPI。还有commons-logging日志系统。也是有基于SPI实现的,我只简单的摘录了几个相关的片段,只不过他好像不是使用了ServiceLoader类去寻找实现,而是自己实现加载文件的。

protected static final String SERVICE_ID = 
    "META-INF/services/org.apache.commons.logging.LogFactory";
if (factory == null) {
            if (isDiagnosticsEnabled()) {
                logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +
                              "] to define the LogFactory subclass to use...");
            }
            try {
                final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);

                if( is != null ) {
                    // This code is needed by EBCDIC and other strange systems.
                    // It's a fix for bugs reported in xerces
                    BufferedReader rd;
                    try {
                        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                    } catch (java.io.UnsupportedEncodingException e) {
                        rd = new BufferedReader(new InputStreamReader(is));
                    }

6、简单总结

其实SPI设计思想也很简单,接地气一点的解释就是,我设计了一个接口,然后自己不实现,当需要使用这个接口的实现时,就用ServiceLoader类去各个依赖的第三方包中扫描,只要扫描到有实现的类,就进行实例化提供服务。对我来说,实现是完全透明的,我只根据接口的方法来编程,正真做到了面向接口编程。所以当我们需要换一种实现时,只要替换一下第三方依赖JAR就行了,我的其他代码就不用动了。这种方式用来给第三方自己扩展是不是很赞。对的。

© 著作权归作者所有

共有 人打赏支持
徐安是个好人
粉丝 9
博文 10
码字总数 7781
作品 4
杭州
私信 提问
Dubbo源码分析(7):SPI扩展机制剖析

我们都是知道一个合格的开源框架对于扩展的支持都要是相当弹性的,Dubbo 也不例外。Dubbo采用微内核+插件体系,使得设计优雅,扩展性强。Dubbo的扩展机制是基于SPI思想来实现的,但是并没有采...

郑加威
2018/09/28
0
0
Dubbo内核实现之SPI简单介绍

Dubbo采用微内核+插件体系,使得设计优雅,扩展性强。那所谓的微内核+插件体系是如何实现的呢!即我们定义了服务接口标准,让厂商去实现(如果不了解spi的请谷歌百度下), jdk通过ServiceLo...

明理萝
2018/08/16
0
0
Dubbo源码分析-SPI的应用

SPI简介 SPI是Service Provider Interface的缩写,即服务提供接口(翻译出来好绕口,还是不翻译的好),实质上是接口,作用是对外提供服务。 SPI是Java的一种插件机制,可以不用修改源代码实现新...

农码人生
2018/07/24
0
0
Dubbo详细介绍与安装使用过程

1 Dubbo介绍 1.1 dubbox简介 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进...

庞陆阳
2016/12/21
43
0
dubbo源码解析-spi(二)

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

肥朝
2018/01/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

day11

architect刘源源
38分钟前
3
0
论学好Linux系统的超级重要性

不知道各位在日常的工作生活中有没有接触过“rm -rf /*”这个命令,因为这个命令搞出来的事情可还不少呢!前段时间就在一个群里看到了有个小伙子,老板让他去维护一下服务器,这小伙也不太懂...

Linux就该这么学
昨天
1
0
git 使用

1,首先在github配置好信息和仓库,然后在本地进行操作 git init git config user.name 'zhangwuer' git config user.email '56789053@qq.com' 2,与远程分支建立连接 git checkout -b test......

天王盖地虎626
昨天
3
0
git checkout 命令详解

在日常的git操作中,git checkout——检出,是我们的常用命令。最为常用的两种情形是创建分支和切换分支。 在下面的命令中,使用了一些简写,在这里说明一下: git st # git statusgit ci ...

shzwork
昨天
8
0
【Nginx】Nginx多级代理,获取客户端真实请求IP以及每级代理IP

Nginx多级代理,获取客户端真实请求IP以及每级代理IP 如图所示,每一级nginx里的location配置里需要加上对应的配置,最后一级nginx是直接到应用,测试时为了方便,直接用echo模块去测试,打印...

薛定谔的旺
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部