目录
1、前言
SPI?一开始接触这个概念大部分人都会很懵,SPI是啥?平时就听过API和SCP(狗头),老夫CRUD这么多年了, 还从来没有听说过Java有这么一个玩意。
然而事实是,你虽然没有听过,但你几乎每天都在享受它给你带来的便利。今天我们就来聊聊这个传说中的SPI机制。
2、SPI的概念
SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用。
听到框架,你是不是心里有一点点印象了呢?
2.1 典型应用:JDBC
最典型的例子就是我们开发中几乎每天都会用到的jdbc。
java.sql.Driver接口由核心类库提供,但是它的实现很明显不是Java提供的,而是各大服务商来提供。
当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。
JDK中查找服务的实现的工具类是:java.util.ServiceLoader。
那为什么配置文件为什么要放在META-INF/services下面?可以打开ServiceLoader的代码,里面定义了文件的PREFIX如下:
private static final String PREFIX = "META-INF/services/"
而我们在引入的jar里也确实能找到这个东西:
2.2 SPI机制的通俗理解
到这里可能大家心里对SPI机制已经有了一个大概的了解,实际上它可以理解为一个规范。
举个通俗一点的例子:我有一台手机,需要打电话但是没得卡怎么办?总不可能我做手机的还得负责把你电话卡问题也解决吧?那后面岂不是还有手机壳、耳机等问题?
很明显手机开发商不会给你一条龙服务,它在出厂的时候定义好卡槽大小,不管你是移动还是联通还是电信,只要你根据我的规范设计好电话卡,那我直接放进去手机就能使用。
3、双亲委派模型
在说SPI破坏了双亲委派模型之前,我们先来了解一下什么是双亲委派模型(摘自周志明大神的《深入理解Java虚拟机第三版》):
3.1 双亲委派模型的好处
4、为什么说SPI破坏了双亲委派模型
双亲委派模型很好的解决了各个类记载器的基础类统一问题(越基础的类由越上层的类加载器加载)。
基础类之所以称为基础,是因为它们总是作为被用户调用的API,但如果基础类又要回调用户的代码,那该怎么办呢。
4.1 可见性原则
我们都知道,Java的类加载器结构是这样的:
![](https://img-blog.csdnimg.cn/20201011103854518.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZveEV4Y2VwdGlvbg==,size_16,color_FFFFFF,t_70#pic_center)
启动类加载器作为应用程序类加载器的上级,启动类加载器加载的类 对应用程序类加载器是可见的。
然而应用程序类加载器加载的类对启动类加载器却是不可见的。
这是由 classloader 加载模型中的可见性(visibility)决定的。可见性原则允许子类加载器查看父ClassLoader加载的所有类,但父类加载器看不到子类加载器的类。
4.2 双亲委派模型的妥协
java.sql.DriverManager通过扫包的方式拿到指定的实现类,完成 DriverManager的初始化。
但是,根据可见性原则,java.sql.DriverManager是启动类加载器负责的,根据双亲委派的可见性原则,启动类加载器加载的 DriverManager 是不可能拿到系统应用类加载器加载的实现类 。
为了解决这个困境,Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器 (Thread Context ClassLoader)。
这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。
通过这个类加载器可以实现功能,但也正是因为如此,双亲委派模型的可见性原则就被破坏了,但这也是无可奈何的事情,所以只能说是妥协~
5、结尾
Java SPI的使用很简单。也做到了基本的加载扩展点的功能。但Java SPI有以下的不足:
- 需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要的实现。
- 配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。
- 扩展如果依赖其他的扩展,做不到自动注入和装配
- 不提供类似于Spring的IOC和AOP功能
- 扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持
这些观点是dubbo官网博客提出的,很明显dubbo的SPI对此做出了改进,至于dubbo具体是怎么做的
传送门在这----->Dubbo可扩展机制实战