SPI的目的
让调用者调用更加方便
SPI和Interface的本质区别
1 SPI的接口定义和接口实现是分开的(不同jar包),SPI就是一种调用场景(没别的)
2 Interface的接口定义和接口实现都在服务提供方
SPI如何实现
1 底层使用JDK的ServiceLoader
(1)配置文件:META-INF/services/接口名称
(2)配置文件内容为具体的实现类的全限定名
2 具体业务逻辑在SPI接口定义方实现
没有SPI的时候(以com.mysql.jdbc.Driver为例)
1 没有SPI:需要手动实例化所需的Driver,新加入一种实现就需要再写一遍
2 有SPI:
Class.forName(jdbcClass); Connection conn = DriverManager.getConnection(jdbcUrl, userName, password);
DriverManager源码
(1)用ServiceLoader加载所有Driver类,调用load方法只是清空了所有的providers
public class DriverManager { static { loadInitialDrivers(); println("JDBC DriverManager initialized"); } private static void loadInitialDrivers() { ... ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ while(driversIterator.hasNext()) { // 调用ServiceLoader的next() driversIterator.next(); } } catch(Throwable t) { // Do nothing } ... } }
(2)核心代码在ServiceLoader中的hasNextService和nextService。
a) hasNextService会调用系统classLoader(AppClassLoader)去所有classpath中d的META-INF/services/ + 接口名文件下找到类名
b) nextService会调用Class.forName加载类
public final class ServiceLoader<S> implements Iterable<S> { public S next() { if (acc == null) { return nextService(); } else { PrivilegedAction<S> action = new PrivilegedAction<S>() { public S run() { return nextService(); } }; return AccessController.doPrivileged(action, acc); } } private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; } private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen } }