前面在深入策略模式系列中,我们简单介绍了策略模式的原理,以及策略模式在jdk线程池中的应用,这里来和大家学习下策略模式在jdk SPI的应用,这里结合SPI的源码来一起学习下。
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。常见的 SPI 有 JDBC、日志门面接口、Spring、SpringBoot相关starter组件、Dubbo、JNDI等。 实际上Java SPI是"面向接口的编程+策略模式+配置文件"组合实现的动态加载机制,在JDK中提供了工具类:"java.util.ServiceLoader"来实现服务查找。我们为了实现各功能模块之间解耦,一般都是基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类的耦合,就违反了可插拔、闭开等原则,如果我们希望实现在模块装配的时候能够不在程序硬编码指定,那就需要一种服务发现的机制。Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦。
首先看ServiceLoader类的签名类的成员变量:
public final class ServiceLoader<S> implements Iterable<S>{ private static final String PREFIX = "META-INF/services/"; // 代表被加载的类或者接口 private final Class<S> service; // 用于定位,加载和实例化providers的类加载器 private final ClassLoader loader; // 创建ServiceLoader时采用的访问控制上下文 private final AccessControlContext acc; // 缓存providers,按实例化的顺序排列 private LinkedHashMap<String,S> providers = new LinkedHashMap<>(); // 懒查找迭代器(内部类,真正加载服务类) private LazyIterator lookupIterator; ...... }load方中初始化了ServiceLoader对象,通过ServiceLoader构造器来实例化了内部类LazyIterator
public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){ //load方中初始化了ServiceLoader对象 return new ServiceLoader<>(service, loader); } public final class ServiceLoader<S> implements Iterable<S> // ServiceLoader构造器 private ServiceLoader(Class<S> svc, ClassLoader cl) { //要加载的接口 service = Objects.requireNonNull(svc, "Service interface cannot be null"); //(ClassLoader类型,类加载器) loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; //(AccessControlContext类型,访问控制器) acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; //(LinkedHashMap<String,S>类型,用于缓存加载成功的类) providers.clear();//先清空 //实例化内部类(实现迭代器功能) LazyIterator lookupIterator = new LazyIterator(service, loader); } }LazyIterator类如下
private class LazyIterator implements Iterator<S>{ Class<S> service; ClassLoader loader; Enumeration<URL> configs = null; Iterator<String> pending = null; String nextName = null; private boolean hasNextService() { //第二次调用的时候,已经解析完成了,直接返回 if (nextName != null) { return true; } if (configs == null) { try { //META-INF/services/ 加上接口的全限定类名,就是文件服务类的文件 //META-INF/services/com.viewscenes.netsupervisor.spi.SPIService String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName);//将文件路径转成URL对象 } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } //解析URL文件对象,读取内容,最后返回 pending = parse(service, configs.nextElement()); } //拿到第一个实现类的类名 nextName = pending.next(); return true; }可以看到上面通过pending.next()拿到实现类的类名
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 S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { //终究还是通过class.forName然后再newInstance()完成实例化的 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 }实例化完成之后, 把实例化后的类缓存到providers对象中,(LinkedHashMap<String,S>类型)然后返回实例对象。