Skip to content

Commit

Permalink
Merge pull request #2379 from mangyimang/main
Browse files Browse the repository at this point in the history
Update spi.md
  • Loading branch information
Snailclimb committed Apr 29, 2024
2 parents c1d2b32 + ec5e3e7 commit c9746ff
Showing 1 changed file with 10 additions and 0 deletions.
10 changes: 10 additions & 0 deletions docs/java/basis/spi.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -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` 中进行查找。
Expand Down

0 comments on commit c9746ff

Please sign in to comment.