概述
SPI
全称 Service Provider Interface
,说白了,就是 Java 只提供接口,而不同厂商根据接口做不同的实现。Java 就可以根据 SPI 机制为某个接口寻找接口的实现类。这样就实现解耦和模块化。常见的 SPI
有 JDBC
、日志门面接口、 Spring
、 SpringBoot
相关starter组件、 Dubbo
、 JNDI
等等。
Demo
定义接口
public interface MyJdbcService {
void connect();
}
服务提供者接口实现
public class MyJdbcServiceImpl implements MyJdbcService{
@Override
public void connect() {
System.out.println("My JDBC Connect.");
}
}
在
**META-INF/services**
目录下创建文件
在META-INF/services
目录下创建一个以 接口全限定名
为命名的文件,内容为实现类的全限定名。
通过 ServiceLoader 获取实现类
public class SpiDemo {
public static void main(String[] args) {
ServiceLoader<MyJdbcService> services = ServiceLoader.load(MyJdbcService.class);
Iterator<MyJdbcService> iterator = services.iterator();
while (iterator.hasNext()) {
MyJdbcService myJdbcService = iterator.next();
myJdbcService.connect();
}
}
}
// OUTPUT
// My JDBC Connect.
源码分析
使用线程上下文来加载
Class
。public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
缺陷
不能按需加载,需要遍历所有实现,并实例化。如果不想用某些实现类,或者某些类实例化比较耗时,但它也被载入了。这就造成性能损失。
- 获取某个实现类的方式不够灵活,只能通过迭代器扣税。
ServiceLoader
实例是非线程安全的。