SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,主要核心就是解耦,一般用于框架的模块化开发,或者可拔插组件开发。
实现类
ch.qos.logback.classic.servlet.LogbackServletContainerInitializer
实现类
org.apache.logging.log4j.util.EnvironmentPropertySource org.apache.logging.log4j.util.SystemPropertiesPropertySource
实现类
org.springframework.web.SpringServletContainerInitializer
通常各大数据库厂商(如Mysql、Oracle)会根据一个统一的规范:java.sql.Driver
开发各自的驱动实现逻辑。客户端使用jdbc时不需要去改变代码,直接引入不同的spi接口服务即可。
JDBC4之前,需要先加载驱动,例如:Class.forName("com.mysql.jdbc.Driver");
JDBC4之后,则不需要再显式通过Class.forName加载驱动,由于DriverManager在静态块通过SPI完成了注册过程,代码如下
static { loadInitialDrivers(); println("JDBC DriverManager initialized"); } private static void loadInitialDrivers() { …… AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); while(driversIterator.hasNext()) { driversIterator.next(); } …… } …… } }
在DriverManager.getConnection(String url,String user,String pass)方法中已经有了spi的实现,会默认去加载驱动类。
此外,Dubbo中也大量用到spi机制。
如图
优点:面向接口的编程,解耦,可拔插,适合于各种组件式开发,商业性开发。
缺点:需要遍历所有的实现,并实例化,资源浪费,获取某个实现类的方式不够灵活,多线程使用 ServiceLoader 类的实例不安全。