Java-ServiceLoader类与SPI机制
引子
对于Java中的Service类和SPI机制的透彻理解,也算是对Java类加载模型的掌握的不错的一个反映。
了解一个不太熟悉的类,那么从使用案例出发,读懂源代码以及代码内部执行逻辑是一个不错的学习方式。
一、使用案例
通常情况下,使用ServiceLoader
来实现SPI机制。 SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制, 比如有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。
SPI机制可以归纳为如下的图:
起始这样说起来还是比较抽象,那么下面举一个具体的例子,案例为JDBC的调用例子:
案例如下:
JDBC中的接口即为:java.sql.Driver
SPI机制的实现核心类为:java.util.ServiceLoader
Provider则为:com.mysql.jdbc.Driver
外层调用则是我们进行增删改查JDBC操作所在的代码块,但是对于那些现在还没有学过JDBC的小伙伴来说(不难学~),这可能会有点难理理解,所以我这里就举一个使用案例:
按照上图的SPI执行逻辑,我们需要写一个接口、至少一个接口的实现类、以及外层调用的测试类。
但是要求以这样的目录书结构来定义项目文件,否则SPI机制无法实现(类加载机制相关,之后会讲):
E:.
│ MyTest.java
│
├─com
│ └─fisherman
│ └─spi
│ │ HelloInterface.java
│ │
│ └─impl
│ HelloJava.java
│ HelloWorld.java
│
└─META-INF
└─services
com.fisherman.spi.HelloInterface
其中:
- MyTest.java为测试java文件,负责外层调用;
- HelloInterface.java为接口文件,等待其他类将其实现;
- HelloJava.java 以及 HelloWorld.java 为接口的实现类;
- META-INF
└─services
com.fisherman.spi.HelloInterface 为配置文件,负责类加载过程中的路径值。
首先给出接口的逻辑:
public interface HelloInterface {
void sayHello();
}
其次,两个实现类的代码:
public class HelloJava implements HelloInterface {
@Override
public void sayHello() {
System.out.println("HelloJava.");
}
}
public class HelloWorld implements HelloInterface {
@Override
public void sayHello() {
System.out.println("HelloWorld.");
}
}
然后,配置文件:com.fisherman.spi.HelloInterface
com.fisherman.spi.impl.HelloWorld
com.fisherman.spi.impl.HelloJava
最后测试文件:
public class MyTest26 {
public static void main(String[] args) {
ServiceLoader<HelloInterface> loaders = ServiceLoader.load(HelloInterface.class);
for (HelloInterface in : loaders) {
in.sayHello();
}
}
}
测试文件运行后的控制台输出:
HelloWorld.
HelloJava.
我们从控制台的打印信息可知我们成功地实现了SPI机制,通过 ServiceLoader 类实现了等待实现的接口和实现其接口的类之间的联