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对象,获取不同的类。