|
|
|
@ -18,6 +18,12 @@ head:
|
|
|
|
|
|
|
|
|
|
为了实现在模块装配的时候不用在程序里面动态指明,这就需要一种服务发现机制。Java SPI 就是提供了这样一个机制:**为某个接口寻找服务实现的机制。这有点类似 IoC 的思想,将装配的控制权移交到了程序之外。**
|
|
|
|
|
|
|
|
|
|
java 也经常会制定一些规范,除了提供默认实现之外,也支持第三方提供其自己对于某规范的实现,例如 JDBC( Java 数据库连接)驱动管理、日志框架、图片处理服务等。以 JDBC 为例,JDBC 4.0 及其之后的版本使用 SPI 机制来自动发现和加载数据库驱动。开发者只需要将 JDBC 驱动的 JAR 包放在类路径下,无需通过 Class.forName() 来显式加载驱动类。
|
|
|
|
|
|
|
|
|
|
但是以上机制存在一个特定于 java 的问题,即双亲委派模型不能很好地处理那些由Java核心库直接加载,但又必须由加载到JVM中的第三方类实现的情况。因为核心类加载器不会去加载那些位于应用程序类路径(classpath)上的类,这就导致了一个问题:如何允许核心 Java API 能够加载由应用程序类加载器加载的类或接口的实现?
|
|
|
|
|
|
|
|
|
|
java 设计了一种机制来解决该问题,这就是 SPI 。
|
|
|
|
|
|
|
|
|
|
## SPI 介绍
|
|
|
|
|
|
|
|
|
|
### 何谓 SPI?
|
|
|
|
@ -332,6 +338,10 @@ public void reload() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
其解决第三方类加载的机制其实就蕴含在 `ClassLoader cl = Thread.currentThread().getContextClassLoader();` 中,`cl` 就是**线程上下文类加载器**(Thread Context ClassLoader)。这是每个线程持有的类加载器,JDK的设计允许应用程序或容器(如Web应用服务器)设置这个类加载器,以便核心类库能够通过它来加载应用程序类。
|
|
|
|
|
|
|
|
|
|
线程上下文类加载器默认情况下是应用程序类加载器(Application ClassLoader),它负责加载classpath上的类。当核心库需要加载应用程序提供的类时,它可以使用线程上下文类加载器来完成。这样,即使是由引导类加载器加载的核心库代码,也能够加载并使用由应用程序类加载器加载的类。
|
|
|
|
|
|
|
|
|
|
根据代码的调用顺序,在 `reload()` 方法中是通过一个内部类 `LazyIterator` 实现的。先继续往下面看。
|
|
|
|
|
|
|
|
|
|
`ServiceLoader` 实现了 `Iterable` 接口的方法后,具有了迭代的能力,在这个 `iterator` 方法被调用时,首先会在 `ServiceLoader` 的 `Provider` 缓存中进行查找,如果缓存中没有命中那么则在 `LazyIterator` 中进行查找。
|
|
|
|
|