0%

Java 类加载机制(四):自定义 ClassLoader

1. Java 类加载机制的强大之处

  • 在于我们可以创建自定义的 ClassLoader
  • 自定义 ClassLoader 是 Tomcat 实现应用隔离、支持 JSP、OSGI 实现动态模块化的基础

2. 怎样自定义 ClassLoader

  • 一般而言,继承类 ClassLoader,重写 findClass() 方法就可以了
  • 实现 findClass() 方法
    • 使用自己的逻辑寻找 class 文件字节码的字节形式
    • 找到后,使用如下方法转换为 Class 对象:protected final Class<?> defineClass(String name, byte[] b, int off, int len)
    • name 表示类名,b 是存放字节码数据的字节数组,有效数据从 off 开始,长度为 len

3. 写一个自定义 ClassLoader 的 Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyClassLoader extends ClassLoader {
private static final String BASE_DIR = "data/c87/"; //从 BASE_DIR 下的路径中加载类
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String fileName = name.replaceAll("\\.", "/");
fileName = BASE_DIR + fileName + ".class";
try {
byte[] bytes = BinaryFileUtils.readFileToByteArray(fileName); //使用第 13 章介绍的 readFileToByteArray() 方法读取文件,转换为 byte 数组
return defineClass(name, bytes, 0, bytes.length);
} catch(IOException ex) {
throw new ClassNotFoundException("failed to load class " + name, ex);
}
}
}

4. 接上题,自定义的 MyClassLoader 有什么用呢

  • MyClassLoader 没有指定父 ClassLoader默认是系统类加载器

    • ClassLoader.getSystemClassLoader() 的返回值
    • 不过,ClassLoader 有一个可重写的构造方法,可以指定父 ClassLoader: protected ClassLoader(ClassLoader parent)
  • 确实可以BASE_DIR 加到 classpath,但从 Web 服务器、数据库和缓存服务器获取 byte 数组,这就不是系统类加载器能做到的了

  • 使用自定义的 MyClassLoader 还有一个好处,就是可以创建多个 MyClassLoader对同一个类,每个 MyClassLoader 都可以加载一次,得到同一个类的不同对象。这样的好处是

    • 可以实现隔离。一个复杂的程序,内部可能按模块组织,不同模块可能使用同一个类,但使用的是不同的版本,如果使用同一个类加载器,它们是无法共存的。不同模块使用不同的类加载器就可以实现隔离,Tomcat 使用它隔离不同的 Web 应用,OSGI 使用它隔离不同模块
    • 可以实现热部署。使用同一个 ClassLoader,类只会被加载一次。加载后,即使 class 文件已经变了,再次加载,得到的也还是原来的 Class 对象。而使用 MyClassLoader,则可以创建一个新的 ClassLoader,再用它加载 Class,得到的 Class 对象就是新的,从而实现动态更新
-------------------- 本文结束感谢您的阅读 --------------------