1 介绍

该类属于mybatis的io包的,主要用来加载类,加载器是有多种,一个类应该用哪个类加载器呢,这个类就是做这个层封装,将多个类加载器进行排序。

2 定义

  1. 自定义类加载器 > 默认类加载器 > 当前线程Id上下文类加载器 > 当前类的加载器 > 系统类加载器
  2. 封装加载类资源有三种,一种返回URL、一种是返回InputStream,最后一种根据类名返回Class
  3. 在实例化该对象的时候默认会赋值系统类加载器

    3 ClassLoader

    ClassLoader 负责加载来自文件系统,网络系统或其他来源的类文件,JAVA虚拟机中的类加载器使用的是双亲委派模式。
    ClassLoaderWrapper之类加载器包装类 - 图1
    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 加载的共享类库。
ClassLoaderWrapper之类加载器包装类 - 图2

5 ClassLoaderWrapper

构造方法有:
image.png

5.1 源码解读

5.1.1 构造方法

  • 即便外面的方法传入的类加载器是一个空对象,依然默认选择系统类加载器。

    1. /**
    2. * 默认类加载器
    3. */
    4. ClassLoader defaultClassLoader;
    5. /**
    6. * 系统类加载器
    7. */
    8. ClassLoader systemClassLoader;
    9. /**
    10. * 在实例化该ClassLoaderWrapper 自动获取系统类加载器
    11. */
    12. ClassLoaderWrapper() {
    13. try {
    14. systemClassLoader = ClassLoader.getSystemClassLoader();
    15. } catch (SecurityException ignored) {
    16. // AccessControlException on Google App Engine
    17. }
    18. }

    5.1.2 getResourceAsURL方法

  • 获取当前类路径的资源作为URL返回

    1. /**
    2. * Get a resource as a URL using the current class path
    3. *
    4. * @param resource - 一个文件路径
    5. * @return the resource or null
    6. */
    7. public URL getResourceAsURL(String resource) {
    8. // 获取当前类路径的资源作为URL返回
    9. return getResourceAsURL(resource, getClassLoaders(null));
    10. }
    11. public URL getResourceAsURL(String resource, ClassLoader classLoader) {
    12. // 获取当前类路径的资源作为URL返回,指定类加载器
    13. return getResourceAsURL(resource, getClassLoaders(classLoader));
    14. }
    15. URL getResourceAsURL(String resource, ClassLoader[] classLoader) {
    16. URL url;
    17. for (ClassLoader cl : classLoader) {
    18. // 也是遍历所有类加载器
    19. if (null != cl) {
    20. // 获取传入的资源
    21. url = cl.getResource(resource);
    22. // 相对路径,添加"/"路径前缀
    23. if (null == url) {
    24. url = cl.getResource("/" + resource);
    25. }
    26. // 资源可以是简单的文件或目录,也可以是对更复杂对象的引用
    27. // 该资源不为空的场合,返回资源
    28. if (null != url) {
    29. return url;
    30. }
    31. }
    32. }
    33. // didn't find it anywhere.
    34. return null;
    35. }

    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) {

      1. // 循环遍历类加载
      2. if (null != cl) {
      3. // 如果类加载器不为空,用jdk自带类加载器去加载对应资源
      4. // try to find the resource as passed
      5. InputStream returnValue = cl.getResourceAsStream(resource);
      6. // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
      7. if (null == returnValue) {
      8. // 如果没有找到,重新以/开头去加载一遍
      9. returnValue = cl.getResourceAsStream("/" + resource);
      10. }
      11. if (null != returnValue) {
      12. // 如果不为空直接返回
      13. return returnValue;
      14. }
      15. }

      } return null; }

  1. <a name="MzLDt"></a>
  2. ### 5.1.4 classForName方法
  3. ```java
  4. Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {
  5. // 根据类名获取对应Class
  6. for (ClassLoader cl : classLoader) {
  7. if (null != cl) {
  8. try {
  9. // 表示所需类的类对象
  10. return Class.forName(name, true, cl);
  11. } catch (ClassNotFoundException e) {
  12. // we'll ignore this until all classloaders fail to locate the class
  13. }
  14. }
  15. }
  16. throw new ClassNotFoundException("Cannot find class: " + name);
  17. }

5.1.5 getResourceAsStream方法

  1. /**
  2. * Try to get a resource from a group of classloaders
  3. * 拿一个类加载对象去加载资源
  4. *
  5. * @param resource - the resource to get
  6. * @param classLoader - the classloaders to examine
  7. * @return the resource or null
  8. */
  9. InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
  10. for (ClassLoader cl : classLoader) {
  11. // 循环遍历类加载
  12. if (null != cl) {
  13. // 如果类加载器不为空,用jdk自带类加载器去加载对应资源
  14. // try to find the resource as passed
  15. InputStream returnValue = cl.getResourceAsStream(resource);
  16. // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
  17. if (null == returnValue) {
  18. // 如果没有找到,重新以/开头去加载一遍
  19. returnValue = cl.getResourceAsStream("/" + resource);
  20. }
  21. if (null != returnValue) {
  22. // 如果不为空直接返回
  23. return returnValue;
  24. }
  25. }
  26. }
  27. return null;
  28. }

5.1.6 getClassLoaders方法

  1. /**
  2. * 这个方法就是该类的核心,类加载器的顺序
  3. * 自定义类加载器 > 默认类加载器 > 当前线程Id上下文类加载器 > 当前类的加载器 > 系统类加载器
  4. *
  5. * @param classLoader
  6. * @return
  7. */
  8. ClassLoader[] getClassLoaders(ClassLoader classLoader) {
  9. return new ClassLoader[]{
  10. classLoader,
  11. defaultClassLoader,
  12. Thread.currentThread().getContextClassLoader(),
  13. getClass().getClassLoader(),
  14. systemClassLoader};
  15. }