本篇文章简要总结和分析下 Java 的 SPI 机制的原理。
1 概述 SPI,全称为 Sevice Provider Interface,是 JDK 内置的一种服务提供发现机制,用于框架扩展和代码替换,主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是解耦;
Android 中的 dagger 依赖注入框架的内部就有 SPI 的应用!
2 基本使用
1 2 3 4 5 6 package com.coolqi.myapplication;public class ICommonInterface { void show (int name) { } }
然后需要在应用的 classpath 目录下创建,META-INF/services/ 目录,同时创建一个以 ICommonInterface 服务接口命名的文件;
1 META-INF/services/com.coolqi.myapplication.ICommonInterface
然后,在 com.coolqi.myapplication.ICommonInterface 文件中记录那些实现了这个借口的类!
1 2 3 com.coolqi.myapplication.CommonInterfaceA com.coolqi.myapplication.CommonInterfaceB com.coolqi.myapplication.CommonInterfaceC
最后我们要通过 JDK 的 ServiceLoader 来查询接口
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.coolqi.myapplication;import java.util.ServiceLoader;public class CoolSPI { public static void main (String[] args) { ServiceLoader<ICommonInterface> serviceLoader = ServiceLoader.load(ICommonInterface.class); for (ICommonInterface helloSPI : serviceLoader) { helloSPI.show(1 ); } } }
上面就是 SPI 的基本使用方法;
3 ServiceLoader - 源码分析 下面我们来看看 ServiceLoader 的源码(JDK 1.8):
3.1 成员属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public final class ServiceLoader <S > implements Iterable <S > { private static final String PREFIX = "META-INF/services/" ; private final Class<S> service; private final ClassLoader loader; private LinkedHashMap<String,S> providers = new LinkedHashMap<>(); private LazyIterator lookupIterator;
对于 ServiceLoader,我们只能通过 foreach 语法糖来访问内部数据,而 foreach 是通过迭代器 Iterator,不断获取next元素,而这里的迭代器是由 LazyIterator lookupIterator 是实现的!
3.2 load - 加载服务 1 2 3 4 5 6 public static <S> ServiceLoader<S> load (Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
这里调用了另外一个参数:
1 2 3 4 5 6 public static <S> ServiceLoader<S> load (Class<S> service, ClassLoader loader) { return new ServiceLoader<>(service, loader); }
3.3 new ServiceLoader 创建 ServiceLoader 实例:
1 2 3 4 5 6 7 8 9 10 private ServiceLoader (Class<S> svc, ClassLoader cl) { service = Objects.requireNonNull(svc, "Service interface cannot be null" ); loader = (cl == null ) ? ClassLoader.getSystemClassLoader() : cl; reload(); }
3.4 reload 清空了缓存,通知创建 LazyIterator 实例:
1 2 3 4 5 public void reload () { providers.clear(); lookupIterator = new LazyIterator(service, loader); }
核心是创建 LazyIterator 实例!
3.5 iterator - 核心方法 ServiceLoader 实现了 iterator 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public Iterator<S> iterator () { return new Iterator<S>() { Iterator<Map.Entry<String,S>> 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(); } }; }
其内部返回了一个新的 Iterator 实例,用遍历数据!
可以看到先访问缓存,再访问 lookupIterator 实例;
4 LazyIterator 懒加载机制的 Iterator,之所以是懒加载,是因为并不是在创建的时候加载文件的,而是在第一次加载的时候,在初始化的;
4.1 成员变量和构造器 1 2 3 4 5 6 7 8 9 private class LazyIterator implements Iterator <S > { Class<S> service; ClassLoader loader; Enumeration<URL> configs = null ; Iterator<String> pending = null ; String nextName = null ;
同时我们来看看构造器:
1 2 3 4 private LazyIterator (Class<S> service, ClassLoader loader) { this .service = service; this .loader = loader; }
不多说了;
4.2 hasNext 判断是否有下一个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public boolean hasNext () { return hasNextService(); }
继续看:
4.2.1 hasNextService 是否有下一个元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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 ; }
方法很简单,不多说了!
4.3 next 返回下一个元素!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public S next () { return nextService(); }
继续看:
4.3.1 nextService 返回下一个服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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" , x); } if (!service.isAssignableFrom(c)) { ClassCastException cce = new ClassCastException( service.getCanonicalName() + " is not assignable from " + c.getCanonicalName()); fail(service, "Provider " + cn + " not a subtype" , cce); } 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(); }
这里调用了 Class.cast 将子类实例的类型转为父类的:
1 2 3 4 5 public T cast (Object obj) { if (obj != null && !isInstance(obj)) throw new ClassCastException(cannotCastMsg(obj)); return (T) obj; }
这里的 T 实际上就是我们上面的 com.coolqi.myapplication.ICommonInterface;
1 2 3 4 5 6 7 public boolean isInstance (Object obj) { if (obj == null ) { return false ; } return isAssignableFrom(obj.getClass()); }
5 总结