第二章 基础支持层(5) 资源加载 ClassLoaderWrapper

在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.存在什么问题?有哪些需要改进的地方?

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值