1 介绍
该类属于mybatis的io包的,主要用来加载类,加载器是有多种,一个类应该用哪个类加载器呢,这个类就是做这个层封装,将多个类加载器进行排序。
2 定义
- 自定义类加载器 > 默认类加载器 > 当前线程Id上下文类加载器 > 当前类的加载器 > 系统类加载器
- 封装加载类资源有三种,一种返回URL、一种是返回InputStream,最后一种根据类名返回Class
-
3 ClassLoader
ClassLoader 负责加载来自文件系统,网络系统或其他来源的类文件,JAVA虚拟机中的类加载器使用的是双亲委派模式。
JAVA 虚拟机中的类加载器默认使用的是双亲委派模式,其中有三种默认的类加载器,BootStrapClassLoader,ExtensionClassLoader,SystemClassLoader(AppClassLoader),每种类加载器都已经确定从哪个位置加载类文件。4 双亲委派
根据双亲委派模式,在加载类文件时,子加载器会首先将加载请求委托给它的父加载器,父加载器会检测自己是否已经加载过该类,如果以加载,则加载过程结束,如果未加载则请求向上传递,直到BootStrap ClassLoader。如果始终未检测到该类被加载,则从BootStrap ClassLoader 开始尝试从其对应的路劲中加载该类文件,如果加载失败,则有子类加载器尝试加载,直至发起请求的子加载器为止。
双亲委派模式可以保证两点- 子加载器可以使用父加载器已加载的类,但父加载器不能使用子加载器加载的类。
- 父加载器已加载过的类,无法被子类再次加载,这样就可以保证JVM的安全性和稳定性。
在很多场景中,系统会使用不同的类加载器完成不同的任务,这里以tomcat为例简单介绍一下。
Tomcat 会为每个部署的应用创建一个唯一的类加载,也就是WebApp ClassLoader,它负责加载该应用的
WEB-INF/lib 目录下的Jar文件以及WEB-INF/classes 目录下的Class 文件。由于每个应用都有自己的WebAppClassLoader。WebAppClassLoader 的父加载器是Common ClassLoader ,所以不同的应用可以使用Common ClassLoader 加载的共享类库。
5 ClassLoaderWrapper
5.1 源码解读
5.1.1 构造方法
即便外面的方法传入的类加载器是一个空对象,依然默认选择系统类加载器。
/**
* 默认类加载器
*/
ClassLoader defaultClassLoader;
/**
* 系统类加载器
*/
ClassLoader systemClassLoader;
/**
* 在实例化该ClassLoaderWrapper 自动获取系统类加载器
*/
ClassLoaderWrapper() {
try {
systemClassLoader = ClassLoader.getSystemClassLoader();
} catch (SecurityException ignored) {
// AccessControlException on Google App Engine
}
}
5.1.2 getResourceAsURL方法
获取当前类路径的资源作为URL返回
/**
* Get a resource as a URL using the current class path
*
* @param resource - 一个文件路径
* @return the resource or null
*/
public URL getResourceAsURL(String resource) {
// 获取当前类路径的资源作为URL返回
return getResourceAsURL(resource, getClassLoaders(null));
}
public URL getResourceAsURL(String resource, ClassLoader classLoader) {
// 获取当前类路径的资源作为URL返回,指定类加载器
return getResourceAsURL(resource, getClassLoaders(classLoader));
}
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;
}
}
}
// didn't find it anywhere.
return null;
}
5.1.3 getResourceAsStream方法
```java public InputStream getResourceAsStream(String resource) { // 从当前类路径下获取资源以InputStream返回 return getResourceAsStream(resource, getClassLoaders(null)); }
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { // 从当前类路径下获取资源以InputStream返回, 指定类加载器 return getResourceAsStream(resource, getClassLoaders(classLoader)); }
/**
- Try to get a resource from a group of classloaders
- 拿一个类加载对象去加载资源 *
- @param resource - the resource to get
- @param classLoader - the classloaders to examine
@return the resource or null */ InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) {
// 循环遍历类加载
if (null != cl) {
// 如果类加载器不为空,用jdk自带类加载器去加载对应资源
// try to find the resource as passed
InputStream returnValue = cl.getResourceAsStream(resource);
// now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
if (null == returnValue) {
// 如果没有找到,重新以/开头去加载一遍
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {
// 如果不为空直接返回
return returnValue;
}
}
} return null; }
<a name="MzLDt"></a>
### 5.1.4 classForName方法
```java
Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
// 根据类名获取对应Class
for (ClassLoader cl : classLoader) {
if (null != cl) {
try {
// 表示所需类的类对象
return Class.forName(name, true, cl);
} catch (ClassNotFoundException e) {
// we'll ignore this until all classloaders fail to locate the class
}
}
}
throw new ClassNotFoundException("Cannot find class: " + name);
}
5.1.5 getResourceAsStream方法
/**
* Try to get a resource from a group of classloaders
* 拿一个类加载对象去加载资源
*
* @param resource - the resource to get
* @param classLoader - the classloaders to examine
* @return the resource or null
*/
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
for (ClassLoader cl : classLoader) {
// 循环遍历类加载
if (null != cl) {
// 如果类加载器不为空,用jdk自带类加载器去加载对应资源
// try to find the resource as passed
InputStream returnValue = cl.getResourceAsStream(resource);
// now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
if (null == returnValue) {
// 如果没有找到,重新以/开头去加载一遍
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {
// 如果不为空直接返回
return returnValue;
}
}
}
return null;
}
5.1.6 getClassLoaders方法
/**
* 这个方法就是该类的核心,类加载器的顺序
* 自定义类加载器 > 默认类加载器 > 当前线程Id上下文类加载器 > 当前类的加载器 > 系统类加载器
*
* @param classLoader
* @return
*/
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{
classLoader,
defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}