Java SPI 总结
1. Java SPI 目标
- 定义好SPI, 方便三方定制扩展, 如JDBC的驱动实现, Dubbo的SPI实现.
- 原生, 脱离Spring等容器框架, 动态加载接口的实现.
2. SPI vs API
-
API (Application Programming Interface)
面向 使用者
-
SPI (Service Provider Interface)
面向 扩展者
3. Java SPI 使用
-
定义接口 demo.java.spi.DemoSpi
package demo.java.spi; public interface DemoSpi { void helloSpi(); }
-
定义实现 demo.java.spi.impl.DemoSpiImpl(V1,V2)
package demo.java.spi.impl; import demo.java.spi.DemoSpi; public class DemoSpiV1Impl implements DemoSpi { public DemoSpiV1Impl() { System.out.println("DemoSpiV1Impl初始化..."); } @Override public void helloSpi() { System.out.println("hello spi v1"); } } public class DemoSpiV2Impl implements DemoSpi { public DemoSpiV2Impl() { System.out.println("DemoSpiV2Impl初始化..."); } @Override public void helloSpi() { System.out.println("hello spi v2"); } }
-
新建resources/META-INF/services/demo.java.spi.DemoSpi
demo.java.spi.impl.DemoSpiV1Impl demo.java.spi.impl.DemoSpiV2Impl
-
demo.java.spi.DemoSpiTest
package demo.java.spi; import java.util.Iterator; import java.util.ServiceLoader; public class DemoSpiTest { public static void main(String[] args) { ServiceLoader<DemoSpi> serviceLoader = ServiceLoader.load(DemoSpi.class); /** * 遍历方式1, 等价于方式2, 但是便于调试 * * 1. (懒加载方式) 只有获取时, 才会 * - 解析配置文件, 获取SPI接口的实现类名 * - Class.forName加载实现类 * - 反射生成实例对象 * * 2. 且有缓存, 多次遍历, 只有第一次, 才会初始化实例对象 */ Iterator<DemoSpi> iterator = serviceLoader.iterator(); while(iterator.hasNext()) { DemoSpi demoSpi = iterator.next(); demoSpi.helloSpi(); } System.out.println("---------------"); // 遍历方式2 for (DemoSpi demoSpi : serviceLoader) { demoSpi.helloSpi(); } } }
-
运行结果
DemoSpiV1Impl初始化... hello spi v1 DemoSpiV2Impl初始化... hello spi v2 --------------- hello spi v1 hello spi v2
4. Java SPI 原理
-
(懒加载方式) 只有获取时, 才会
- 解析配置文件, 获取SPI接口的实现类名
- Class.forName加载实现类
- 反射生成实例对象
-
且有缓存, 多次遍历, 只有第一次, 才会初始化实例对象