本文共 1994 字,大约阅读时间需要 6 分钟。
SPI?一开始接触这个概念大部分人都会很懵,SPI是啥?平时就听过API和SCP(狗头),老夫CRUD这么多年了, 还从来没有听说过Java有这么一个玩意。
然而事实是,你虽然没有听过,但你几乎每天都在享受它给你带来的便利。今天我们就来聊聊这个传说中的SPI机制。
SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用。
听到框架,你是不是心里有一点点印象了呢?
最典型的例子就是我们开发中几乎每天都会用到的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里也确实能找到这个东西:
到这里可能大家心里对SPI机制已经有了一个大概的了解,实际上它可以理解为一个规范。
举个通俗一点的例子:我有一台手机,需要打电话但是没得卡怎么办?总不可能我做手机的还得负责把你电话卡问题也解决吧?那后面岂不是还有手机壳、耳机等问题?
很明显手机开发商不会给你一条龙服务,它在出厂的时候定义好卡槽大小,不管你是移动还是联通还是电信,只要你根据我的规范设计好电话卡,那我直接放进去手机就能使用。
在说SPI破坏了双亲委派模型之前,我们先来了解一下什么是双亲委派模型(摘自周志明大神的《深入理解Java虚拟机第三版》):
双亲委派模型很好的解决了各个类记载器的基础类统一问题(越基础的类由越上层的类加载器加载)。
基础类之所以称为基础,是因为它们总是作为被用户调用的API,但如果基础类又要回调用户的代码,那该怎么办呢。
我们都知道,Java的类加载器结构是这样的:
启动类加载器作为应用程序类加载器的上级,启动类加载器加载的类 对应用程序类加载器是可见的。
然而应用程序类加载器加载的类对启动类加载器却是不可见的。
这是由 classloader 加载模型中的可见性(visibility)决定的。可见性原则允许子类加载器查看父ClassLoader加载的所有类,但父类加载器看不到子类加载器的类。
java.sql.DriverManager通过扫包的方式拿到指定的实现类,完成 DriverManager的初始化。
但是,根据可见性原则,java.sql.DriverManager是启动类加载器负责的,根据双亲委派的可见性原则,启动类加载器加载的 DriverManager 是不可能拿到系统应用类加载器加载的实现类 。
为了解决这个困境,Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器 (Thread Context ClassLoader)。
这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。
通过这个类加载器可以实现功能,但也正是因为如此,双亲委派模型的可见性原则就被破坏了,但这也是无可奈何的事情,所以只能说是妥协~
Java SPI的使用很简单。也做到了基本的加载扩展点的功能。但Java SPI有以下的不足:
这些观点是dubbo官网博客提出的,很明显dubbo的SPI对此做出了改进,至于dubbo具体是怎么做的
传送门在这----->
转载地址:http://jmfmf.baihongyu.com/