Java中的SPI机制

SPI 全称为 (Service Provider Interface) ,是Java 1.6之后内置的一种服务提供发现机制。SPI可以通过配置来替换服务(或者说interface)的实现;比如java.sql.Driver接口,可以很轻松的从MySQL切换到MongoDB实现。

问题的核心在于,如何根据interface查找对应的实现。

SPI的实现

Java 1.6中,开发者只需要在META-INF/services下添加文件,即可切换、修改对应interface的实现。文件名为实现的接口,文件内容为N个实现的类名,按行分隔。比如:

$ cat src/main/resources/META-INF/services/com.robberphex.Plugin 
com.robberphex.impl.PluginImpl1
com.robberphex.impl.PluginImpl2

使用起来,主要就是ServiceLoader类

ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class);
for (Plugin plugin : loader) {
    System.out.println(plugin.getClass().getCanonicalName());
}

要注意的是,ServiceLoader#load方法是CallerSensitive的,即load的时候用的是调用者的类加载器。

Java 9

Java 9之后,由于模块化的引入,所以SPI机制也做了扩展:除了原有的通过META-INF/services来注册服务外,还可以通过module中的provides…with语句来注册服务:

# module-info.java
module com.robberphex.impl1 {
    requires com.robberphex.interfacex;
    exports com.robberphex.impl1;
    provides com.robberphex.Plugin with PluginImpl1;
}

而且除了可以将interface的实现提供出去以外,还可以用一个接口来注册:

# PluginProvider.java
public interface PluginProvider {
    static Plugin provider() {
        return new PluginImpl2();
    }
}
# module-info.java
module com.robberphex.impl2 {
    requires com.robberphex.interfacex;
    exports com.robberphex.impl2;
    provides com.robberphex.Plugin with PluginProvider;
}

详细的例子可以参考Github


参考资料:

  1. https://zhuanlan.zhihu.com/p/31767780
  2. https://openjdk.java.net/projects/jigsaw/quick-start
  3. https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html
  4. https://openjdk.java.net/projects/jigsaw/spec/

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.