目录
基础环境
JDK 1.8
概念
SPI(Service Provider Interface): 服务提供接口,是JDK提供的一种服务加载机制,简单理解就是通过此JDK规范可实现服务的发现并加载。主要有三部分组成:
Service: 服务,更准确来说是服务定义,可以接口、抽象类,同样可以是普通的类(服务提供者为其子类,不过一般不这么来定义)
Service Provider: 服务提供者,可以是Service接口实现类,Service抽象类的实现,以及Service类的继承
ServiceLoader: 一个简单的服务提供者加载工具
原始的参考资料可参考JDK源码中对java.util.ServiceLoader的描述。
实践
SPI规范
- 配置文件路径: Service Provider声明配置文件必须在META-INF/services 目录下;
- 配置文件名称: Service接口、实现类等全限定类名
- 配置文件内容:Service实现类(一个实现类一行)
- 保证实现类jar包被正确加载(放置classpath中或自定义Class Loader保证加载)
- 实现类提供无参构造方法
参考示例:
SPI设计思想
面向接口编程、接口隔离原则(ISP)
代码实践
项目结构:
定义Serivece
- 接口cn.com.demo.api.ISpiDemo
package cn.com.demo.api;
public interface ISpiDemo {
String name();
String exec(String msg);
}
- 抽象类 cn.com.demo.api.AnstractSpiDemo
package cn.com.demo.api;
public abstract class AnstractSpiDemo {
public abstract String sayHello();
}
- 普通类 cn.com.demo.api.SpiDemo
package cn.com.demo.api;
public class SpiDemo {
public String sayHello(){
return this.getClass().getSimpleName();
}
}
定义Serivece Provider
接口实现类(定义三个)
cn.com.demo.impl.Hello0ISpiDemo
package cn.com.demo.impl;
import cn.com.demo.api.ISpiDemo;
public class Hello0ISpiDemo implements ISpiDemo {
@Override
public String name() {
return this.getClass().getSimpleName();
}
@Override
public String exec(String msg) {
return name()+msg;
}
}
cn.com.demo.impl.Hello1ISpiDemo
package cn.com.demo.impl;
import cn.com.demo.api.ISpiDemo;
public class Hello1ISpiDemo implements ISpiDemo {
@Override
public String name() {
return this.getClass().getSimpleName();
}
@Override
public String exec(String msg) {
return name()+msg;
}
}
cn.com.demo.impl.Hello2ISpiDemo
package cn.com.demo.impl;
import cn.com.demo.api.ISpiDemo;
public class Hello2ISpiDemo implements ISpiDemo {
@Override
public String name() {
return this.getClass().getSimpleName();
}
@Override
public String exec(String msg) {
return name()+msg;
}
}
META-INF/services/下定义
文件名:cn.com.demo.api.ISpiDemo
文件内容:
cn.com.demo.impl.Hello1ISpiDemo
cn.com.demo.impl.Hello2ISpiDemo
注意: 这里的cn.com.demo.impl.Hello0ISpiDemo未被定义到services文件中,应用将不会注册此类。
抽象类实现类
cn.com.demo.impl.AnstractSpiDemoImpl
package cn.com.demo.impl;
import cn.com.demo.api.AnstractSpiDemo;
public class AnstractSpiDemoImpl extends AnstractSpiDemo {
@Override
public String sayHello() {
return this.getClass().getSimpleName();
}
}
META-INF/services/下定义
文件名:cn.com.demo.api.AnstractSpiDemo
文件内容:
cn.com.demo.impl.AnstractSpiDemoImpl
子类重写
cn.com.demo.impl.SpiDemoChild
package cn.com.demo.impl;
import cn.com.demo.api.SpiDemo;
public class SpiDemoChild extends SpiDemo {
@Override
public String sayHello() {
return this.getClass().getSimpleName();
}
}
META-INF/services/下定义
文件名:cn.com.demo.api.SpiDemo
文件内容:
cn.com.demo.impl.SpiDemoChild
编写测试应用
cn.com.demo.app.SpiTest
package cn.com.demo.app;
import cn.com.demo.api.ISpiDemo;
import cn.com.demo.api.SpiDemo;
import cn.com.demo.api.AnstractSpiDemo;
import lombok.extern.java.Log;
import java.util.ServiceLoader;
@Log
public class SpiTest {
public static void main(String[] args) {
ServiceLoader<ISpiDemo> load = ServiceLoader.load(ISpiDemo.class);
for (ISpiDemo spiDemo : load) {
log.info(spiDemo.name()+"-"+spiDemo);
}
log.info("------------------------------");
ServiceLoader<AnstractSpiDemo> loader = ServiceLoader.load(AnstractSpiDemo.class);
for (AnstractSpiDemo spiDemoC : loader) {
log.info(spiDemoC.sayHello());
}
log.info("------------------------------");
ServiceLoader<SpiDemo> loader1 = ServiceLoader.load(SpiDemo.class);
for (SpiDemo spiDemo : loader1) {
log.info(spiDemo.sayHello());
}
}
}
执行结果
扩展使用
- 利用spi的加载机制,在Class Loader加载和实例化时,可在静态代码块、非静态代码块、无参构造等处做服务的初始化工作,如JDBC4.0规范之后不再需要Class.forName()来注册驱动就是因为在静态代码块中已经完成了DriverManager.registerDriver(new Driver())。
代码地址
spidemo:https://gitcode.net/master336/spidemo/