一、简介
ServiceLoader是Java提供的一个服务提供者实现类加载器,通过在资源目录META-INF/services中放置提供者配置文件(配置文件名称服务提供者接口类完整名称)来标识服务提供者
如Java提供java.sql.Driver提供者接口,不同的数据库厂家Mysql、Oracle各自实现java.sql.Driver接口,譬如MySql提供java.sql.Driver接口实现如下图所示,下一篇会对Driver等类进行分析。基于Java8。
二、属性
//ServiceLoader加载服务提供者实现类的资源目录,存放服务提供者实现类完整路径名称的配置文件的文件名称就是服务提供者接口,如上图/META-INF/services/java.sql.Driver
private static final String PREFIX = "META-INF/services/";
//服务提供者接口Class对象,如java.sql.Driver类的Class对象
private final Class<S> service;
//类加载器,对ClassLoader不清楚的可以看下https://juejin.cn/user/2647279730952478对ClassLoader的讲解
private final ClassLoader loader;
//访问权限控制的上下文,不清楚的可以看下一个老哥的讲解https://juejin.cn/post/6844903657775824910
private final AccessControlContext acc;
//存放服务提供者接口的所有实现类集合,key为服务提供者实现类的完整路径名,value为服务提供者实现类的实例
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
//目录/META-INF/services/配置文件中服务提供者懒加载的迭代器,只有在调用lookupIterator的next方法,才会开始对服务提供者实现类进行初始化,可以看下下面的详细介绍
private LazyIterator lookupIterator;
三、内部类
//目录/META-INF/services/配置文件中服务提供者懒加载的迭代器
private class LazyIterator
implements Iterator<S>
{
//服务提供者接口Class对象,如java.sql.Driver类的Class对象
Class<S> service;
//类加载器,加载这些服务提供者实现类
ClassLoader loader;
//配置文件URL对应的Enumeration对象
Enumeration<URL> configs = null;
//存放服务提供者接口的所有实现类完整路径名称集合的迭代器
Iterator<String> pending = null;
//下一个服务提供者实现类名称
String nextName = null;
//传入服务提供者接口Class对象,及加载这些服务提供者实现类的加载器构建懒加载的迭代器
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
//存放服务提供者接口的所有实现类完整路径名称集合的迭代器,是否有下一个服务
private boolean hasNextService() {
//nextName会在下面nextService方法将其重置为空,如果调用了hasNextService方法,没有调用nextService方法,而是再调用hasNextService方法,会直接返回还有下一个服务
if (nextName != null) {
//返回成功,还有下一个服务
return true;
}
//由于第一次调用,存放服务提供者实现类配置文件URL对应的Enumeration对象configs为空
if (configs == null) {
try {
//获取到存放所有服务提供者实现类的配置文件完整路径,如Mysql的Driver提供者实现类存放路径META-INF/services/java.sql.Driver
String fullName = PREFIX + service.getName();
//如果ClassLoader传进来为空
if (loader == null)
//直接使用传入存放所有服务提供者实现类的配置文件完整路径,获取到存放服务提供者实现类配置文件URL对应的Enumeration对象configs
configs = ClassLoader.getSystemResources(fullName);
else
//否则的话,传入存放所有服务提供者实现类的配置文件完整路径,使用不为空的ClassLoader实例调用getResources方法
configs = loader.getResources(fullName);
} catch (IOException x) {
//如果出现IO异常,直接调用下面介绍的fail进行异常的记录
fail(service, "Error locating configuration files", x);
}
}
//由于第一次调用,存放服务提供者接口的所有实现类完整路径名称集合的迭代器为空,或者迭代器已经没有下一个元素
while ((pending == null) || !pending.hasNext()) {
//如果配置文件URL对应的Enumeration对象也没有下一个元素,直接返回失败,没有下一个服务
if (!configs.hasMoreElements()) {
//返回失败,没有下一个服务
return false;
}
//解析存放服务提供者接口的所有实现类配置文件,校验配置文件中的每一行数据格式是否合法,返回存放服务提供者接口的所有实现类完整路径名称集合的迭代器,在下面会对parse方法进行介绍
pending = parse(service, configs.nextElement());
}
//从存放服务提供者接口的所有实现类完整路径名称集合的迭代器获取下一个服务
nextName = pending.next();
//返回成功,有下一个服务
return true;
}
//获取下一个服务提供者实现类,调用此方法前应该先调用hasNextService方法判断是否有下一个服务提供者实现类,否则会抛出NoSuchElementException异常
private S nextService() {
//调用hasNextService方法判断是否有下一个服务提供者实现类,如果没有抛出NoSuchElementException异常
if (!hasNextService())
//抛出NoSuchElementException异常
throw new NoSuchElementException();
//获取调用上面的hasNextService方法给nextName赋的值,下一个服务提供者实现类的完整名称
String cn = nextName;
//将存放下一个提供者实现类的完整名称的nextName置为空,调用hasNextService会对其重新进行赋值
nextName = null;
Class<?> c = null;
try {
//传入服务提供者实现类的完整名称和传入进来的ClassLoader使用Class.forName获取服务提供者实现类的Class对象
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
//如果在根据提供者实现类的完整名称加载找不到类,使用下面介绍的fail进行异常信息的记录,fail在下面会进行介绍
fail(service,
"Provider " + cn + " not found");
}
//服务提供者接口service是c表示的类或接口的父类,否则的话调用fail方法记录异常信息
if (!service.isAssignableFrom(c)) {
//调用fail方法进行异常的记录,fail方法可以看下面的介绍
fail(service,
"Provider " + cn + " not a subtype");
}
try {
//使用服务提供者接口Class对象将其服务提供者的实现类向上转型,cast内部也是直接强转,有兴趣的可以看下Class的cast源码
S p = service.cast(c.newInstance());
//将服务提供者实现类实例添加到存放服务提供者接口的所有实现类集合,key为服务提供者实现类的完整路径名,value为服务提供者实现类的实例
providers.put(cn, p);
//返回服务提供者实现类实例
return p;
} catch (Throwable x) {
//调用fail方法进行异常的记录,fail方法可以看下面的介绍
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
//抛出Error错误
throw new Error(); // This cannot happen
}
//判断是否有下一个服务提供者实现类
public boolean hasNext() {
//如果访问权限控制的上下文等于空,直接调用上面介绍的hasNextService方法,hasNextService方法可以看上面的介绍
if (acc == null) {
//调用上面介绍的hasNextService方法
return hasNextService();
} else {
//如果访问权限控制的上下文不为空,直接开启一个特权调用上面介绍的hasNextService方法,无需经过访问的权限控制
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() {
//调用上面介绍的hasNextService方法
return hasNextService();
}
};
//使用访问权限控制器调用特权方法
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
//如果访问权限控制的上下文等于空,直接调用上面介绍的nextService方法,nextService方法可以看上面的介绍
if (acc == null) {
//调用上面介绍的nextService方法,获取服务提供者接口的一个实现类实例
return nextService();
} else {
//如果访问权限控制的上下文不为空,直接开启一个特权调用上面介绍的nextService方法,无需经过访问的权限控制
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() {
//调用上面介绍的nextService方法,获取服务提供者接口的一个实现类实例
return nextService();
}
};
//使用访问权限控制器调用特权方法
return AccessController.doPrivileged(action, acc);
}
}
//懒加载迭代器,不支持对存放服务提供者接口的所有实现类集合的移除操作
public void remove() {
//抛出UnsupportedOperationException异常
throw new UnsupportedOperationException();
}
}
四、构造函数
//传入服务提供者接口的Class对象svc和要加载服务提供者接口的所有实现类的ClassLoader对象,私有的构造方法,只能通过调用静态load方法获取ServiceLoader实例
private ServiceLoader(Class<S> svc, ClassLoader cl) {
//判断传入服务提供者接口的Class对象svc不为空,否则抛出NullPointerException异常
service = Objects.requireNonNull(svc, "Service interface cannot be null");
//如果传入进来的要加载服务提供者接口的所有实现类的ClassLoader对象为空,将loader赋值为Applicationclassloader,对ClassLoader不清楚的可以看下https://juejin.cn/user/2647279730952478对ClassLoader的讲解
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
//如果系统的安全管理器不为空,将acc赋值为访问权限控制器的上下文,安全管理器对权限的控制都是基于访问权限控制器进行控制,访问权限控制的上下文,不清楚的可以看下一个老哥的讲解https://juejin.cn/post/6844903657775824910
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
//清空存放服务提供者接口的所有实现类集合, 传入服务提供者接口Class对象,及加载这些服务提供者实现类的加载器构造懒加载的迭代器,看下下面reload方法的介绍
reload();
}
五、加载提供者
//由于ServiceLoader是私有的构造方法,只能通过调用静态load方法获取ServiceLoader实例,传入服务提供者接口的Class对象service和要加载服务提供者接口的所有实现类的ClassLoader对象loader
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader)
{
//使用ServiceLoader私有的构造方法,返回ServiceLoader实例,不清楚的可以看上面的ServiceLoader私有构造方法
return new ServiceLoader<>(service, loader);
}
//由于ServiceLoader是私有的构造方法,只能通过调用静态load方法获取ServiceLoader实例,传入服务提供者接口的Class对象service
public static <S> ServiceLoader<S> load(Class<S> service) {
//获取加载当前线程对象的ClassLoader对象,即ApplicationClassLoader对象
ClassLoader cl = Thread.currentThread().getContextClassLoader();
//传入服务提供者接口的Class对象service和要加载服务提供者接口的所有实现类的ClassLoader对象,即加载当前线程对象的ClassLoader对象,返回ServiceLoader实例
return ServiceLoader.load(service, cl);
}
public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
//获取加载应用的ClassLoader对象ApplicationClassLoader实例对象
ClassLoader cl = ClassLoader.getSystemClassLoader();
//prev会获取到ExtClassLoader的ClassLoader对象
ClassLoader prev = null;
//循环的获取ApplicationClassLoader实例对象的父ClassLoader,即ExtClassLoader
while (cl != null) {
prev = cl;
//当前ClassLoader获取父的ClassLoader,如果一个ClassLoader的父ClassLoader是BootstrapClassLoader,getParent就会返回空,因为BootstrapClassLoader为C++进行实现
cl = cl.getParent();
}
////传入服务提供者接口的Class对象service和要加载服务提供者接口的所有实现类的ClassLoader对象,即ExtClassLoader实例对象,返回ServiceLoader实例
return ServiceLoader.load(service, prev);
}
//清空存放服务提供者接口的所有实现类集合, 传入服务提供者接口Class对象,及加载这些服务提供者实现类的加载器构造懒加载的迭代器
public void reload() {
//清空存放服务提供者接口的所有实现类集合
providers.clear();
//传入服务提供者接口Class对象,及加载这些服务提供者实现类的加载器构造懒加载的迭代器
lookupIterator = new LazyIterator(service, loader);
}
六、服务提供者迭代器
//获取所有服务提供者接口的实现类的迭代器,因为ServiceLoader也是Iterable接口的实现类,因此需要重写iterator方法
public Iterator<S> iterator() {
//直接返回Iterator的匿名内部类
return new Iterator<S>() {
//获取存放服务提供者接口的所有实现类集合的迭代器
Iterator<Map.Entry<String,S>> knownProviders
= providers.entrySet().iterator();
//返回是否有下一个服务提供者接口的实现类实例
public boolean hasNext() {
//由于第一次调用放服务提供者接口的所有实现类集合providers还没有一个元素,为此会调用上面介绍的lookupIterator.hasNext(),从配置文件中解析出实现服务提供者接口的的所有实现类的完整类名集合的迭代器,然后判断迭代器中是否还有服务提供者接口的的实现类
if (knownProviders.hasNext())
//再一次调用ServiceLoader的iterator方法获取Iterator实现类,调用hasNext方法,providers集合已经有元素,无需再调用ookupIterator.hasNext()方法
return true;
//从配置文件中解析出实现服务提供者接口的的所有实现类的完整类名集合的迭代器,然后判断迭代器中是否还有服务提供者接口的的实现类,对lookupIterator.hasNext()方法不清楚的可以看下上面对lookupIterator类的介绍
return lookupIterator.hasNext();
}
//获取下一个服务提供者接口的的实现类
public S next() {
//由于第一次调用放服务提供者接口的所有实现类集合providers还没有一个元素,为此会调用上面介绍的lookupIterator.next()方法
//lookupIterator.next()获取调用上面的hasNextService方法给nextName赋的值,传入服务提供者实现类的完整名称和传入进来的ClassLoader使用Class.forName获取服务提供者实现类的Class对象
//对服务提供者的接口实现类进行实例化,然后将其实现类实例化加入到存放服务提供者接口的所有实现类集合providers,返回服务提供者接口的实现类实例
//不清楚的可以看下上面lookupIterator.next()方法的介绍
if (knownProviders.hasNext())
//再一次调用ServiceLoader的iterator方法获取Iterator实现类,调用next()方法,providers集合已经有元素,无需再调用lookupIterator.next()方法
return knownProviders.next().getValue();
//lookupIterator.next()获取调用上面的hasNextService方法给nextName赋的值,传入服务提供者实现类的完整名称和传入进来的ClassLoader使用Class.forName获取服务提供者实现类的Class对象
//对服务提供者的接口实现类进行实例化,然后将其实现类实例化加入到存放服务提供者接口的所有实现类集合providers,返回服务提供者接口的实现类实例
//不清楚的可以看下上面lookupIterator.next()方法的介绍
return lookupIterator.next();
}
//不支持对存放服务提供者接口的所有实现类集合的元素做移除操作
public void remove() {
//抛出UnsupportedOperationException异常
throw new UnsupportedOperationException();
}
};
}
七、其他方法
//传入服务提供者接口Class对象service和失败的描述信息msg,以及导致错误的错误对象cause,抛出ServiceConfigurationError异常
private static void fail(Class<?> service, String msg, Throwable cause) throws ServiceConfigurationError
{
//抛出ServiceConfigurationError异常,错误信息为拼接传入服务提供者接口的名称加上失败的描述信息、以及导致错误的错误对象cause信息
throw new ServiceConfigurationError(service.getName() + ": " + msg,
cause);
}
//传入服务提供者接口Class对象service和失败的描述信息msg,抛出ServiceConfigurationError错误
private static void fail(Class<?> service, String msg) throws ServiceConfigurationError
{
//抛出ServiceConfigurationError异常,错误信息为拼接传入服务提供者接口的名称加上失败的描述信息
throw new ServiceConfigurationError(service.getName() + ": " + msg);
}
//传入服务提供者接口Class对象service和失败的描述信息msg以及存放所有服务提供者实现类完整名称的配置文件url,以及错误的信息是配置文件中的那一行数据,抛出ServiceConfigurationError异常
private static void fail(Class<?> service, URL u, int line, String msg) throws ServiceConfigurationError
{
//调用上面介绍的传入服务提供者接口Class对象service和失败的描述信息msg的fail方法
fail(service, u + ":" + line + ": " + msg);
}
//解析存放所有服务提供者实现类完整名称的配置文件的每一行数据,校验每行数据的格式是否有包含非法字符,或者包含java中的关键字,每行数据就是服务提供者接口的某个个实现类的完整路径名
//@param service 服务提供者接口Class对象service,为了校验每个实例化后的实现类是否是服务提供者接口的实现类,并且Class对象将实现类向上转型为服务提供者接口
//@param u 配置文件的url,url目的是为了记录那个配置文件出问题
//@param r 配置文件的内容BufferedReader
//@param lc 目前处于配置文件的那一行,也是为了记录配置文件那行出问题
//@param names 存放所有服务提供者接口的实现类的完整名称
//@return 配置文件的下一行
//@throw {IOException,解析配置文件出现IO异常。ServiceConfigurationError,配置文件的某一行数据格式错误,或者Java的关键字,抛出ServiceConfigurationError错误}
private int parseLine(Class<?> service, URL u, BufferedReader r, int lc, List<String> names) throws IOException, ServiceConfigurationError
{
//读取配置文件的下一行数据
String ln = r.readLine();
//如果没有读取行数据,返回-1
if (ln == null) {
//返回-1
return -1;
}
如果行数据第一个#的所在位置
int ci = ln.indexOf('#');
//如果行数据存在#,截取行数据从头截到第一个#号所在的位置,得到新的行数据
if (ci >= 0) ln = ln.substring(0, ci);
//去除行数前后的空格
ln = ln.trim();
//获取行数据的长度
int n = ln.length();
//行数据长度不等于0
if (n != 0) {
//如果行数据中包含' '或者包含换行符,直接抛出ServiceConfigurationError错误
if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
//调用上面介绍的fail方法,抛出ServiceConfigurationError错误
fail(service, u, lc, "Illegal configuration-file syntax");
//获取行数据的第一个元素所在的ASCII码值
int cp = ln.codePointAt(0);
//判断行数据的第一个元素的字符是否允许在Java标识符的第一个字符,比如包名的首字符不能为数字
if (!Character.isJavaIdentifierStart(cp))
//调用上面介绍的fail方法,抛出ServiceConfigurationError错误
fail(service, u, lc, "Illegal provider-class name: " + ln);
//从行数据的第二个元素开始循环行数据的所有元素
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
//获取行数据每个元素的ASCII码值
cp = ln.codePointAt(i);
//确定cp是Java标识符中除首字符以外的部分,否则抛出ServiceConfigurationError错误
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
//调用上面介绍的fail方法,抛出ServiceConfigurationError错误
fail(service, u, lc, "Illegal provider-class name: " + ln);
}
//存放服务提供者接口的所有实现类集合providers的key没有包含行数据(服务提供者的其中一个实现类完整路径名称),并且存放所有服务提供者的其中一个实现类完整路径名称也包含行数据
if (!providers.containsKey(ln) && !names.contains(ln))
//将行数据(服务提供者的其中一个实现类完整路径名称)添加到names集合中
names.add(ln);
}
//返回配置文件的下一行的行数
return lc + 1;
}
//解析配置文件
//@param service 服务提供者接口的Class对象
//@param u 配置文件的来源URL
private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError
{
InputStream in = null;
BufferedReader r = null;
//存放所有服务提供者接口的实现类完整路径名称names集合
ArrayList<String> names = new ArrayList<>();
try {
//将URL以流的形式打开
in = u.openStream();
//将配置文件的工作流以UTF8包装成BufferedReader对象
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
//从配置文件的第一行行数据进行解析,校验
int lc = 1;
//循环的解析配置文件的每一行行数据,直到读取完配置文件的所有内容
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
//如果读取配置文件出现IO异常,使用上面介绍的fail方法组装错误信息抛出ServiceConfigurationError错误
fail(service, "Error reading configuration file", x);
} finally {
try {
//关闭InputStream工作流
if (r != null) r.close();
//关闭BufferedReader
if (in != null) in.close();
} catch (IOException y) {
//如果在关闭工作流的过程中出现IO异常,使用上面介绍的fail方法组装错误信息抛出ServiceConfigurationError错误
fail(service, "Error closing configuration file", y);
}
}
//返回存放所有服务提供者接口的实现类完整路径名称names集合迭代器
return names.iterator();
}
//重写Object的toString方法
public String toString() {
//返回java.util.ServiceLoader串拼接[和服务提供者接口名称以及]
return "java.util.ServiceLoader[" + service.getName() + "]";
}