java SPI理解
What?
SPI机制(Service Provider Interface)其实源自服务提供者框架(Service Provider Framework,参考【EffectiveJava】page6),是一种将服务接口与服务实现分离以达到解耦、
大大提升了程序可扩展性的机制。引入服务提供者就是引入了spi接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔。
典型实例:jdbc的设计
通常各大厂商(如Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑。客户端使用jdbc时不需要去改变代码,直接引入不同的spi接口服务即可。
jdbc连接源码分析
1. java.sql.DriverManager静态块初始执行,其中使用spi机制加载jdbc具体实现
//java.sql.DriverManager.java //当调用DriverManager.getConnection(..)时,static会在getConnection(..)执行之前被触发执行 /** * Load the initial JDBC drivers by checking the System property * jdbc.properties and then use the {@code ServiceLoader} mechanism */ static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }
private static void loadInitialDrivers() { String drivers; AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //查找具体的provider,就是在META-INF/services/***.Driver文件中查找具体的实现。 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); //查找具体的实现类的全限定名称 try{ while(driversIterator.hasNext()) { driversIterator.next();//加载并初始化实现类 } } catch(Throwable t) { // Do nothing } return null; } }); } }
//写死的一个目录 private static final String PREFIX = "META-INF/services/"; private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { String fullName = PREFIX + service.getName(); //通过相对路径读取classpath中META-INF目录的文件,也就是读取服务提供者的实现类全限定名 if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } //判断是否读取到实现类全限定名,比如mysql的“com.mysql.jdbc.Driver ” while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next();//nextName保存,后续初始化实现类使用 return true; }
public S next() { if (acc == null) {//用来判断serviceLoader对象是否完成初始化 return nextService(); } else { PrivilegedAction<S> action = new PrivilegedAction<S>() { public S run() { return nextService(); } }; return AccessController.doPrivileged(action, acc); } } private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName;//上一步找到的服务实现者全限定名 nextName = null; Class<?> c = null; try { //加载字节码返回class对象.但并不去初始化(换句话就是说不去执行这个类中的static块与static变量初始化) // 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 { //初始化这个实现类.将会通过static块的方式触发实现类注册到DriverManager(其中组合了一个CopyOnWriteArrayList的registeredDrivers成员变量)中 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 }
上一步中,Sp = service.cast(c.newInstance()) 将会导致具体实现者的初始化,比如mysqlJDBC,会触发如下代码:
//com.mysql.jdbc.Driver.java ...... private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); ...... static { try { //并发安全的想一个copyOnWriteList中方 java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
总结:
SPI是通过读取指定文件找到接口实现类的全类名,并通过全类名反射创建对象实例
touch fish

浙公网安备 33010602011771号