挖源码走起!!
ServiceLoader
首先这个类就是用来实现spi的,我先放一行实现spi的代码ServiceLoader
public static ServiceLoader load(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return new ServiceLoader(Reflection.getCallerClass(), service, cl);
}
load方法主要是返回一个ServiceLoader对象,其中获取了一个上下文加载器,这个就是app加载器。为什么这么说,Launch类里面在构造器把app加载器set为了ContextClassLoader,具体看《双亲委派最细解析,挖一下源码吧》这篇博客。那么就构建了ServiceLoader对象。我们调用一下ServiceLoader的iterator方法就会返回对应的实现类。
public Iterator iterator() {
// create lookup iterator if needed
if (lookupIterator1 == null) {
lookupIterator1 = newLookupIterator();
}
return new Iterator() {
// 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;
}
};
}
iterator方法会调用newLookupIterator方法,newLookupIterator方法返回LazyClassPathLookupIterator对象。我们看看这个对象。LazyClassPathLookupIterator是个内部类,里面就规定了AAA这个接口的实现类信息定义的路径,就是类路径下的META-INF/services/,而这个路径下必须有文件,文件名为AAA的全路径名(如com.CmJava.AAA),文件内部写实现类的全路径名(如com.CmJava.AAAImpl)。
private final class LazyClassPathLookupIterator
implements Iterator
{
static final String PREFIX = "META-INF/services/";
Set providerNames = new HashSet(); // to avoid duplicates
Enumeration configs;
Iterator pending;
Provider nextProvider;
ServiceConfigurationError nextError;
LazyClassPathLookupIterator() { }
}
因此iterator方法最终会返回AAA的实现类对象。而接口还是实现类都是由app加载器进行加载的,并不会受到双亲委派的影响。
小结
如果需要使用SPI,首先要在META-INF/services/定义实现类信息,文件名是接口全路径,文件内是实现类全路径。那么不管双亲委派还是全盘委托的方式都不会影响这些类的加载,都是用app加载器加载的
评论区