在MyBatis的IO包中封装了ClassLoader以及读取资源文件相关的API。
在ClassLoaderWrapper是一个ClassLoader的包装器
,其中包含了多个ClassLoader对象。通过调整多个类加载器的使用顺序。使用ClassLoaderWrapper就如同使用ClassLoader对象,ClassLoaderWrapper 会按照指定的顺序依次检测其中封装的ClassLoader对象,并从中选取第一个可用的ClassLoader完成相关功能。
ClassLoaderWrapper中定义了两个字段,分别记录了系统指定的默认加载器(defaultClassLoader)和系统加载器(systemClassLoader)
ClassLoader defaultClassLoader;//默认类的加载器,初始为null
ClassLoader systemClassLoader;// 系统类加载器
【注意】defaultClassLoader 和 systemClassLoader的修饰符为包内可见,defaultClassLoader会在同一包下的Resources 中初始化。
ClassLoaderWrapper的主要功能分为三类:
getResourceAsURL()
getResourceAsStream()
classFormName()
这三个方法逻辑很相似,这里以classFormName()为例进行介绍
public Class> classForName(String name) throws ClassNotFoundException {
return classForName(name, getClassLoaders(null));
}
public Class> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
return classForName(name, getClassLoaders(classLoader));
}
真正被调用的方法
private Class> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
for (ClassLoader cl : classLoader) {//遍历所有加载器
if (null != cl) {
try {
Class> c = Class.forName(name, true, cl);//使用Class.forName加载一个类
if (null != c) {//直到加载到Class返回
return c;
}
} catch (ClassNotFoundException e) {
// we'll ignore this until all classloaders fail to locate the class
}
}
}// 所有类加载器都加载不到,抛出ClassNotFoundException
throw new ClassNotFoundException("Cannot find class: " + name);
}
//增加 private 修饰
// 返回ClassLoader[] 数组,该数组指明了类加载器的使用顺序
private ClassLoader[] getClassLoaders(ClassLoader classLoader) {
ClassLoader[] classLoaders=new ClassLoader[]{
classLoader,//由参数指定的默认类加载器
defaultClassLoader,//系统指定的默认类加载器
Thread.currentThread().getContextClassLoader(),//当前线程绑定的类加载器
getClass().getClassLoader(),//加载当前类所使用的类加载器
systemClassLoader};//系统类加载器
return classLoaders;
}
完整源码:
package org.apache.ibatis.io;
import java.io.InputStream;
import java.net.URL;
/**
* 用于包装对多个类加载器的访问的类,使它们作为一个整合
* 该类仅供Resources使用
*/
public class ClassLoaderWrapper {
ClassLoader defaultClassLoader;//默认类加载器,默认为为null,会被Resources 初始化
ClassLoader systemClassLoader;// 系统类加载器
public ClassLoaderWrapper() {
try {
//初始化系统类加载器
systemClassLoader = ClassLoader.getSystemClassLoader();
} catch (SecurityException ignored) {
//为什么要捕获SecurityException 异常
}
}
//getResourceAsURL, URL 有什么用?
public URL getResourceAsURL(String resource) {
return getResourceAsURL(resource, getClassLoaders(null));
}
public URL getResourceAsURL(String resource, ClassLoader classLoader) {
return getResourceAsURL(resource, getClassLoaders(classLoader));
}
private URL getResourceAsURL(String resource, ClassLoader[] classLoader) {
URL url;
for (ClassLoader cl : classLoader) {
if (null != cl) {
url = cl.getResource(resource);
if (null == url) {
url = cl.getResource("/" + resource);
}
if (null != url) {
return url;
}
}
}
return null;
}
//getResourceAsStream: 底层调用了ClassLoader.getResourceAsStream()
public InputStream getResourceAsStream(String resource) {
return getResourceAsStream(resource, getClassLoaders(null));
}
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
return getResourceAsStream(resource, getClassLoaders(classLoader));
}
private InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
for (ClassLoader cl : classLoader) {
if (null != cl) {
// 核心,调用了ClassLoader.getResourceAsStream()
InputStream returnValue = cl.getResourceAsStream(resource);
if (null == returnValue) {
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {
return returnValue;
}
}
}
return null;
}
//classForName
public Class> classForName(String name) throws ClassNotFoundException {
return classForName(name, getClassLoaders(null));
}
public Class> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
return classForName(name, getClassLoaders(classLoader));
}
private Class> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
for (ClassLoader cl : classLoader) {
if (null != cl) {
try {
Class> c = Class.forName(name, true, cl);//使用Class.forName加载一个类
if (null != c) {
return c;
}
} catch (ClassNotFoundException e) {
// we'll ignore this until all classloaders fail to locate the class
}
}
}
throw new ClassNotFoundException("Cannot find class: " + name);
}
//增加 private 修饰
private ClassLoader[] getClassLoaders(ClassLoader classLoader) {
ClassLoader[] classLoaders=new ClassLoader[]{
classLoader,//由参数指定的默认类加载器
defaultClassLoader,//系统指定的默认类加载器
Thread.currentThread().getContextClassLoader(),//当前线程绑定的类加载器
getClass().getClassLoader(),//加载当前类所使用的类加载器
systemClassLoader};//系统类加载器
return classLoaders;
}
}
Resources 是提供了多个静态方法的工具类,其中一个封装了有个ClassLoaderWrapper类型的静态字段,Resources 提供的静态方法都是通过调用ClassLoaderWrapper的方法实现,代码比较简单,就不贴出来了。
public class Resources {
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
......
}
总结:
1.为什么使用ClassLoaderWrapper而不是ClassLoader?ClassLoaderWrapper的核心在哪里?
框架中加载资源的场景非常普遍,有类加载,文件加载(如配置文件,db.properties等)等,这些操作的普遍需要判断文件、路径,加载器是否为空,还要抛出异常,将这些判断与异常的处理封装起来,简化调用逻辑,使得上层代码的结构更清晰。其实这种包装在框架中会普遍存在,JDK 代码只是提供了实现,并没有考虑业务场景的多样性。
2.存在什么问题?有哪些需要改进的地方?