概述
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{@Overridepublic 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实例是非线程安全的。
