文本通过实现自定义类加载器,实践 Java 类加载的流程。
阅读此文前,需要了解 Java 类加载的基本原理,参见如下两篇博文:
以上博文中所提及的 Java 类加载机制,都是 Java1.2 及以后的版本,而在最早的 Java1.1 中类加载器是没有父子关系的模式的。这里将分别对 Java1.1 和 Java1.2 及以后的类加载版本进行展示。
Java1.1 中的实现
原理介绍
Java1.1 的类加载机制相对单一,用户自定义加载器的重写比较复杂。
主要需要重写加载器中的 Class loadClass(String name)方法。
Class loadClass(String name)或 loadClass(String name , boolean resolve)方法是加载的核心。它根据类的全名(比如 String 类的全名是 java.lang.String)获得对应类的二进制数据,然后通过 Class defineClass(byte[] b) 将二进制数据加载到 JVM 的方法区,并返回对应类的 Class 实例,然后根据可选的参数 resolve 决定是否需要现在解析这个类。最后将这个 Class 实例作为 loadClass 方法的返回值。
如果无法加载和 defineClass,即无法通过本加载器直接加载类的情况,则使用 Class findSystemClass(String name) 将类加载任务委派给系统类加载器查找。如果能找到则加载,否则抛出 ClassNotFoundException 异常。
编程实例
以下用实例来展示这一过程:
类 CompilingClassLoader 是一个自定义加载器,它能直接读取 Java 源文件实现类加载。CLL 类的 main 方法为程序入口,通过 ComplilingClassLoader 加载一个 Foo 类,使用反射机制调用 Foo 类的 main 方法。
CompilingClassLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
import java.io.*;
/*
A CompilingClassLoader compiles your Java source on-the-fly. It
checks for nonexistent .class files, or .class files that are older
than their corresponding source code.
*/
public class CompilingClassLoader extends ClassLoader
{
// Given a filename, read the entirety of that file from disk
// and return it as a byte array.
private byte[] getBytes( String filename ) throws IOException {
// Find out the length of the file
File file = new File( filename );
long len = file.length();
// Create an array that's just the right size for the file's
// contents
byte raw[] = new byte[(int)len];
// Open the file
FileInputStream fin = new FileInputStream( file );
// Read all of it into the array; if we don't get all,
// then it's an error.
int r = fin.read( raw );
if (r != len)
throw new IOException( "Can't read all, "+r+" != "+len );
// Don't forget to close the file!
fin.close();
// And finally return the file contents as an array
return raw;
}
// Spawn a process to compile the java source code file
// specified in the 'javaFile' parameter. Return a true if
// the compilation worked, false otherwise.
private boolean compile( String javaFile ) throws IOException {
// Let the user know what's going on
System.out.println( "CCL: Compiling "+javaFile+"..." );
|