24.4 自定义ClassLoader
24.4 自定义ClassLoader
Java类加载机制的强大之处在于,我们可以创建自定义的ClassLoader,自定义Class-Loader是Tomcat实现应用隔离、支持JSP、OSGI实现动态模块化的基础。
怎么自定义呢?一般而言,继承类ClassLoader,重写findClass就可以了。怎么实现findClass呢?使用自己的逻辑寻找class文件字节码的字节形式,找到后,使用如下方法转换为Class对象:
1 | protected final Class<? > defineClass(String name, byte[] b, int off, int len) |
name表示类名,b是存放字节码数据的字节数组,有效数据从off开始,长度为len。看个例子:
1 | public class MyClassLoader extends ClassLoader { |
MyClassLoader从BASE_DIR下的路径中加载类,它使用了我们在第13章介绍的readFileToByteArray方法读取文件,转换为byte数组。MyClassLoader没有指定父Class-Loader,默认是系统类加载器,即ClassLoader.getSystemClassLoader()的返回值,不过,Class-Loader有一个可重写的构造方法,可以指定父ClassLoader:
1 | protected ClassLoader(ClassLoader parent) |
MyClassLoader有什么用呢?将BASE_DIR加到classpath中不就行了,确实可以,这里主要是演示基本用法,实际中,可以从Web服务器、数据库或缓存服务器获取bytes数组,这就不是系统类加载器能做到的了。
不过,不把BASE_DIR放到classpath中,而是使用MyClassLoader加载,还有一个很大的好处,那就是可以创建多个MyClassLoader,对同一个类,每个MyClassLoader都可以加载一次,得到同一个类的不同Class对象,比如:
1 | MyClassLoader cl1 = new MyClassLoader(); |
cl1和cl2是两个不同的ClassLoader, class1和class2对应的类名一样,但它们是不同的对象。
但,这到底有什么用呢?
1)可以实现隔离。一个复杂的程序,内部可能按模块组织,不同模块可能使用同一个类,但使用的是不同版本,如果使用同一个类加载器,它们是无法共存的,不同模块使用不同的类加载器就可以实现隔离,Tomcat使用它隔离不同的Web应用,OSGI使用它隔离不同模块。
2)可以实现热部署。使用同一个ClassLoader,类只会被加载一次,加载后,即使class文件已经变了,再次加载,得到的也还是原来的Class对象,而使用MyClassLoader,则可以先创建一个新的ClassLoader,再用它加载Class,得到的Class对象就是新的,从而实现动态更新。
下面,我们来具体看热部署的示例。