18.2.2 创建并使用自定义的类加载器
18.2.3 创建并使用自定义的类加载器
JVM中除根类加载器之外的所有类加载器都是ClassLoader子类的实例,开发者可以通过扩展ClassLoader的子类,并重写该ClassLoader所包含的方法来实现自定义的类加载器。查阅API文档中关于ClassLoader的方法不难发现, ClassLoader中包含了大量的protected方法,这些方法都可被子类重写。
ClassLoader类关键方法
ClassLoader类有如下两个关键方法。
| 方法 | 描述 |
|---|---|
loadClass(String name, boolean resolve) |
该方法为ClassLoader的入口点,根据指定名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的 Class对象。 |
findClass(String name) |
根据指定名称来查找类 |
重写findClass方法自定义ClassLoader
如果需要实现自定义的ClassLoader,则可以通过重写以上两个方法来实现,通常推荐重写findClass()方法,而不是重写loadClass()方法。
loadClass方法的执行步骤
loadClass()方法的执行步骤如下。
- 用
findClass(String)来检查是否已经加载类,如果已经加载则直接返回. - 在父类加载器上调用
loadClass()方法。如果父类加载器为null,则使用根类加载器来加载。 - 调用
findClass(String)方法查找类。
从上面步骤中可以看出,重写findClass()方法可以避免覆盖默认类加载器的父类委托、缓冲机制两种策略;如果重写loadClass()方法,则实现逻辑更为复杂。
defineClass方法
在ClassLoader里还有一个核心的defineClass方法
| 方法 | 描述 |
|---|---|
Class defineClass(String name, byte[] b, int off,int len) |
将指定类的字节码文件(即Class文件,如Hello.class)读入字节数组b内,并把它转换为Class对象,该字节码文件可以来源于文件、网络等. |
defineClass方法管理JVM的许多复杂的实现,它负责将字节码分析成运行时数据结构,并校验有效性等。不过不用担心该方法是final方法,所以程序员无须重写该方法。
ClassLoader普通方法
除此之外, ClassLoader里还包含如下一些普通方法。
| 方法 | 描述 |
|---|---|
protected Class<?> findSystemClass(String name) |
从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass方法将原始字节转换成Class对象,以将该文件转换成类 |
static ClassLoader getSystemClassLoader() |
返回系统类加载器。 |
ClassLoader getParent() |
获取该类加载器的父类加载器。 |
protected void resolveClass(Class<?> c) |
链接指定的类。类加载器可以使用此方法来链接类c。读者无须理会关于此方法的太多细节。 |
protected Class<?> findLoadedClass(String name) |
如果此Java虚拟机已加载了名为name的类,则直接返回该类对应的Class实例,否则返回null。该方法是Java类加载缓存机制的体现。 |
程序示例 自定义ClassLoader
下面程序开发了一个自定义的ClassLoader,该ClassLoader通过重写findClass()方法来实现自定义的类加载机制。这个ClassLoader可以在加载类之前先编译该类的源文件,从而实现运行Java之前先编译该程序的目标,这样即可通过该ClassLoader直接运行Java源文件。
1 | import java.io.*; |
上面程序中的重写了findClass()方法,通过重写该方法就可以实现自定义的类加载机制。在本类的findClass()方法中先检查需要加载类的Class文件是否存在,如果不存在则先编译源文件,再调用ClassLoader的defineClass()方法来加载这个Class文件,并生成相应的Class对象。
接下来可以随意提供一个简单的主类,该主类无须编译就可以使用上面的CompileClassLoader来运行它。
1 | public class Hello |
无须编译该Hello.java,可以直接使用如下命令来运行该Hello.java程序
1 | java CompileClassLoader Hello 这是命令行参数 |
运行结果如下:
1 | G:\Desktop\codes\18\18.2>java CompileClassLoader Hello 这是命令行参数 |
本示例程序提供的类加载器功能比较简单,仅仅提供了在运行之前先编译Java源文件的功能。
自定义类加载器可以实现什么功能
实际上,使用自定义的类加载器,可以实现如下常见功能。
- 执行代码前自动验证数字签名
- 根据用户提供的密码解密代码,从而可以实现代码混淆器来避免反编译
*.class文件。 - 根据用户需求来动态地加载类。
- 根据应用需求把其他数据以字节码的形式加载到应用中。