Java SPI
SPI全称为(Service Provider Interface), 是JDK内置的一种服务提供发现机制. SPI是一种动态替换发现的机制. 比如有个接口, 想运行时动态的给它添加实现, 你只需要添加一个实现. 比如经常遇到java.sql.Driver接口, 不同厂商针对同一接口做出不同的实现提供给用户, 而Java的SPI机制可以为某个接口寻找服务实现.
1. 应用实例
当服务的提供者提供了一种接口的实现之后, 需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件, 这个文件里的内容就是这个接口的具体的实现类. 当其他的程序需要这个服务的时候, 就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件, 配置文件中有接口的具体实现类名, 可以根据这个类名进行加载实例化, 就可以使用该服务了. JDK中查找服务实现的工具类是: java.util.ServiceLoader.
新建一个maven项目, 在com.annwyn.spi包下定义一个接口以及两个实现类.
package com.annwyn.spi;
public interface I {
String method();
}
public class A1 implements I {
String method() {
return "A1";
}
}
public class A2 implements I {
String method() {
return "A2";
}
}
之后在resources目录下创建/META-INF/services, 并创建一个名为com.annwyn.spi.I的文件, 文件中写入具体实现类.
com.annwyn.spi.A1
com.annwyn.spi.A2
之后创建测试类进行测试
public class JunitTest {
@Test
public void test() {
ServiceLoader<I> serviceLoader = ServiceLoader.load(I.class);
for(I template : serviceLoader) {
System.out.println(template.method());
}
}
}
正常情况下, 以上方法输出以下内容
A1
A2
2. ServiceLoader缺陷
// 调用该方法前, 都从providers缓存中获取, 如果没有获取到, 则调用改方法
private S nextService() {
......
Class c = Class.forName(cn, false, loader);
// 每次循环迭代时, 都会实例化实现类
S p = service.cast(c.newInstance());
providers.put(cn, p); // 放入缓存
return p;
}
JDK提供的ServiceLoader并不是没有缺陷的. 在循环迭代获取指定实现类时, 会调用nextService方法实例化配置文件中的所有实例类, 并放入缓存(LinkedHashMap<String, Service>)中, 如果只使用接口的其中一个实现类, 那么没有必要去实例化其他实现类.
为此, 可以自己实现一个ServiceLoader, 修改为获取某个实现类时将其进行实例化即可. 此外可以对SPI进行扩充, 加入实现类的生命范围(单例和多例).
浙公网安备 33010602011771号