JDK的SPI用法
1,创建com.test.Log接口,创建实现类com.test.impl.Log4j,com.test.impl.Logback
2,在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件。比如com.test.Log
3,文件内容:
com.test.impl.Log4j com.test.impl.Logback
4,JDK的spi在查找扩展实现类的过程中,需要遍历 SPI 配置文件中定义的所有实现类,该过程中会将这些实现类全部实例化。
5,用法:
# 创建服务加载器 ServiceLoader<Log> serviceLoader = ServiceLoader.load(Log.class); Iterator<Log> iterator = serviceLoader.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); }
jdk11下源码解析
1,创建服务加载器
ServiceLoader<Log> serviceLoader = ServiceLoader.load(Log.class);
2,执行serviceLoader.iterator();
public Iterator<S> iterator() { // create lookup iterator if needed if (lookupIterator1 == null) { lookupIterator1 = newLookupIterator(); } # 创建一个迭代器匿名实现类 return new Iterator<S>() { // record reload count final int expectedReloadCount = ServiceLoader.this.reloadCount; // index into the cached providers list int index; /** * Throws ConcurrentModificationException if the list of cached * providers has been cleared by reload. */ private void checkReloadCount() { if (ServiceLoader.this.reloadCount != expectedReloadCount) throw new ConcurrentModificationException(); } @Override public boolean hasNext() { checkReloadCount(); if (index < instantiatedProviders.size()) return true; return lookupIterator1.hasNext(); } @Override public S next() { checkReloadCount(); S next; if (index < instantiatedProviders.size()) { next = instantiatedProviders.get(index); } else { next = lookupIterator1.next().get(); instantiatedProviders.add(next); } index++; return next; } }; }
一 执行serviceLoader.iterator()->hasNext()->lookupIterator1.hasNext();
二 执行LazyClassPathLookupIterator#hasNext()->hasNextService();
->nextProviderClass()
1 Enumeration<URL> configs = loader.getResources(fullName); 加载资源
fullName="META-INF/services/com.study.utils.spi.demo.KeyGenerator"
2 遍历configs ,解析url 资源
pending = parse(configs.nextElement());
private Iterator<String> parse(URL u) { Set<String> names = new LinkedHashSet<>(); // preserve insertion order try { # 打开连接 URLConnection uc = u.openConnection(); uc.setUseCaches(false); try (InputStream in = uc.getInputStream(); BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"))) { int lc = 1; while ((lc = parseLine(u, r, lc, names)) >= 0); } } catch (IOException x) { fail(service, "Error accessing configuration file", x); } return names.iterator(); }
解析url 资源,读取每一行,放入Set集合。返回Set#iteator()
部分源码:
while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return null; } # 解析url 资源,读取每一行,放入Set集合。返回Set#iteator() pending = parse(configs.nextElement()); } String cn = pending.next(); try { # 得到 Class对象。 return Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); return null; }
也就是说:
执行LazyClassPathLookupIterator#hasNext()->hasNextService()->nextProviderClass()
得到了META-INF/services/目录下以com.test.Log为文件名,解析其内容得到了Class对象。
扩展
class1.isAssignableFrom(class2) 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回 true;否则返回 false。
hasNextService()部分源码
Class<?> clazz = nextProviderClass(); if (clazz == null) return false; if (clazz.getModule().isNamed()) { // ignore class if in named module continue; } if (service.isAssignableFrom(clazz)) { Class<? extends S> type = (Class<? extends S>) clazz; Constructor<? extends S> ctor = (Constructor<? extends S>)getConstructor(clazz); ProviderImpl<S> p = new ProviderImpl<S>(service, type, ctor, acc); nextProvider = (ProviderImpl<T>) p; } else { fail(service, clazz.getName() + " not a subtype"); }
将Class的构造器对象封装到Provider<T> nextProvider 对象中了。也就是说调用hasNext()方法,得到了nextProvider 对象。(相当于得到了Class的构造器对象)
调用next()方法
->LazyClassPathLookupIterator#next()
源码解析:next = lookupIterator1.next().get();
public S next() { checkReloadCount(); S next; if (index < instantiatedProviders.size()) { next = instantiatedProviders.get(index); } else { # 第一次遍历进入这里 next = lookupIterator1.next().get(); # 缓存对象 instantiatedProviders.add(next); } index++; return next; } # lookupIterator1.next()下 Provider<T> provider = nextProvider; if (provider != null) { nextProvider = null; return provider; } # lookupIterator1.next().get() # provider#get public S get() { if (factoryMethod != null) { return invokeFactoryMethod(); } else { # 创建对象 return newInstance(); } }
总结:
1,调用hasNext() 会读配置文件内容,创建构造器对象,放入一个临时provider对象中
2,调用next()方法,会从临时provider对象中,取出构造器对象,创建配置文件内容中的对象实例。
3,ServiceLoader一旦load了某个接口,然后next() 取出了某个对象实例,那么就会缓存起来。缓存到instantiatedProviders对象中。
// The lazy-lookup iterator for iterator operations private Iterator<Provider<S>> lookupIterator1; // 缓存已经加载的对象。 private final List<S> instantiatedProviders = new ArrayList<>();
思路:读取classPath下配置文件中内容,反射生成类。根据不同的class对象,获取不同的类。