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 passedInputStream 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 resourceif (null == returnValue) {// 如果没有找到,重新以/开头去加载一遍returnValue = cl.getResourceAsStream("/" + resource);}if (null != returnValue) {// 如果不为空直接返回return returnValue;}}
} return null; }
<a name="MzLDt"></a>### 5.1.4 classForName方法```javaClass<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {// 根据类名获取对应Classfor (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 passedInputStream 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 resourceif (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};}
