简介
SPI全名为Service Provider Interface 作用:为接口自动寻找实现类 实现方式: 1.标准制定者制定接口 2.不同厂商编写针对于该接口的实现类, 并在jar的“classpath:META-INF/services/全接口名称” 文件中指定相应的实现类全类名 3.开发者直接引入相应的jar,就可以实现为接口自动寻找实现类的功能
代码示例
Log接口
public interface Log { void execute();}复制代码
Log4j类
public class Log4j implements Log { @Override public void execute() { System.out.println("log4j..."); }}复制代码
Logback类
public class Logback implements Log { @Override public void execute() { System.out.println("logback ..."); }}复制代码
Main类
public class Main { public static void main(String[] args) { ServiceLoaderserviceLoader = ServiceLoader.load(Log.class); Iterator iterator = serviceLoader.iterator(); while (iterator.hasNext()) { Log log = iterator.next(); log.execute(); } }}复制代码
在resources下创建META-INF/services目录并创建Log全路径名的文件com.ye.spi.Log文件里写实现类全路径,可以写多个写多个时,可以自己实现逻辑选择对应实现com.ye.spi.Log4j
直接启动Main类就可以得到想要的效果
源码解析
public class Main { public static void main(String[] args) { ServiceLoaderserviceLoader = ServiceLoader.load(Log.class); Iterator iterator = serviceLoader.iterator(); while (iterator.hasNext()) { Log log = iterator.next(); log.execute(); } }}复制代码
1. ServiceLoader.load()2. serviceLoader.iterator()3. iterator.hasNext()4. iterator.next()
第一个方法是ServiceLoader.load()
先看ServiceLoader的属性 private static final String PREFIX="META-INF/services/";//定义实现类的接口文件所在的目录 private final Class service;//接口 private final ClassLoader loader;//定位、加载、实例化实现类 private final AccessControlContext acc;//权限控制上下文 private LinkedHashMap providers = new LinkedHashMap<>();//以初始化的顺序缓存 <接口全名称, 实现类实例> private LazyIterator lookupIterator;//真正进行迭代的迭代器 接口全名称,>
public staticServiceLoaderload(Classservice) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }public staticServiceLoaderload(Classservice, ClassLoader loader) { return new ServiceLoader<>(service, loader); }复制代码
看上面两段代码,load方法最终是调用传参的构造函数new ServiceLoader<>(service, loader) 再继续看new ServiceLoader<>(service, loader)代码
private ServiceLoader(Classsvc, ClassLoader cl) { service = Objects.requireNonNull(svc, "Service interface cannot be null"); loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; reload(); }public void reload() { providers.clear(); lookupIterator = new LazyIterator(service, loader); }private LazyIterator(Classservice, ClassLoader loader) { this.service = service; this.loader = loader; } 复制代码
1. 赋值ServiceLoader的service2. 赋值ServiceLoader的loader3. 调用reload方法4. 清空ServiceLoade的providers5. new LazyIterator对象
综上所述,ServiceLoader.load()其实就是初始化了ServiceLoader对象和LazyIterator对象
第二个方法是serviceLoader.iterator()
public Iteratoriterator() { return new Iterator() { Iterator> knownProviders = providers.entrySet().iterator(); public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); } public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public void remove() { throw new UnsupportedOperationException(); } }; }复制代码
获取迭代器, 初始化knownProviders,第一次是空的 实现了hasNext方法和next方法
iterator.hasNext()
public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); }复制代码
先判断knownProviders有没有,如果有就返回true如果没有调用lookupIterator.hasNext()可以认为knownProviders是一个缓存,后面会看到什么时候有值的
先看一下lookupIterator即LazyIterator这个类的属性 Class service;//接口 ClassLoader loader;//类加载器 Enumeration configs = null;//存放配置文件 Iterator pending = null;//存放配置文件中的内容,并存储为ArrayList,即存储多个实现类名称 String nextName = null;//当前处理的实现类名称
LazyIterator.hasNext()方法
public boolean hasNext() { if (acc == null) { return hasNextService(); } else { PrivilegedActionaction = new PrivilegedAction () { public Boolean run() { return hasNextService(); } }; 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; }复制代码
其实调用的是LazyIterator.hasNextService()方法1. nextName如果有,直接返回true2. 找到对应目录文件3. 解析文件内容4. 赋值给nextName5. 返回true
LazyIterator.next()
public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public S next() { if (acc == null) { return nextService(); } else { PrivilegedActionaction = new PrivilegedAction() { 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 { 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 } 复制代码
1. 先判断knownProviders有没有,如果有就直接取2. 调用nextService获取3. 通过nextName加载对应的类,即实例化4. 把实例化的对象放入providers中,下次就可以在这里面取,不用在实例化了5. 返回实例化的对象
小结:spi大致流程就讲完了
1. 初始化 ServiceLoader和LazyIterator2. 通过load传入的接口找到对应目录的文件3. 解析文件找到类全路径名4. 通过全路径名加载实例化类5. 放入缓存并返回该对象