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
文件。 - 根据用户需求来动态地加载类。
- 根据应用需求把其他数据以字节码的形式加载到应用中。