Java的ClassLoader
Java中的ClassLoader采用Delegation机制。即每一个ClassLoader都有自己的Parent Class Loader,当从一个Class Loader中加载一个Class时,会先到当前ClassLoader的Parent中寻找该Class是否已经被加载,如果是,则从其Parent Class Loader中得到Class的Instance,如果没有,使用当前ClassLoader来加载Class。请看如下程序:
public class Test {
public static void main(String[] args) throws Exception {
Test test = new Test();
ClassLoader cl = test.getClass().getClassLoader();
while (cl != null) {
System.out.println("class loader is: " + cl.toString());
cl = cl.getParent();
}
}
}
在Windows上使用JDK1.5输出如下:
class loader is: sun.misc.Launcher$AppClassLoader@82ba41
class loader is: sun.misc.Launcher$ExtClassLoader@923e30
对于每一个Java进程都有一个System Class Loader,也叫Application Class Loader,用于加载写在CLASSPATH中的Class,并作为其它用户Class Loader的缺省的Parent Class Loader。由以上输出可以看到,Test类由System Class Loader加载,System Class Loader的Parent是Ext. Class Loader。Ext. Class Loader还具有一个Parent Class Loader,就是BootstrapClassLoader。该Class Loader是Java VM内置的Class Loader,用于加载java.*。如果使用ClassLoader.getParent()方法,当一个Class Loader的Parent是Bootstrap是,一般返回null。(Windows, Linux, Solaris均如此)
观察如下程序:
public class B {
public void method1() {
A a = new A();
...
}
}
public class B {
public void method1() {
Class clazz = Class.forName("A");
Object o = clazz.newInstance();
...
}
}
这两段程序都会从“Current Class Loader”来加载Class A。即从加载B Class的Class Loader来加载Class A。
Thread 的Context Class Loader
自从JDK1.2以后,Sun 为java.lang.thread加入了setContextClassLoader和getContextClassLoader两个方法,但是并没有指明应该如何使用。通常比较Confuse的是,当执行上述代码是,是从Thread的Context Class Loader中加载Class A,还是从加载Class B的Class Loader中加载A(如果两个ClassLoader不同的话)?答案是从加载B的Class Loader中加载A,而不是从Thread的Context Class Loader中加载A。那Context Class Loader是做什么用的呢?举例来说:在JDK1.4中,加入了XML的支持(JAXP),用户可以指定DocumentBuilder的Concrete Class,该Class需实现JAXP接口。在DocumentBuilderFactory中是使用Class.forName加载该Concrete Class的。DocumentBuilderFactory是使用Bootstrap Class Loader来加载的,但通常JAXP的实现都是位于CLASSPATH中,是由System Class Loader加载,而Bootstrap Class Loader又是System Class Loader的Ancestor Class Loader,即Bootstrap Class Loader不能从System Class Loader中加载Class。这时,就可以显式的使用Thread.getContextClassLoader来解决这一问题。而在JDK1.4中也正式这样实现的。
这里,我们再看一段程序:
public class Test {
public static void main(String[] args) throws Exception {
Test test = new Test();
ClassLoader cl = test.getClass().getClassLoader();
while (cl != null) {
System.out.println("class loader is: " + cl.toString());
cl = cl.getParent();
}
System.out.println("thread context class loader is: " + Thread.currentThread().getContextClassLoader());
System.out.println("system class loader is: " + ClassLoader.getSystemClassLoader());
Class clazz = Class.forName("java.lang.String");
System.out.println("bootstrap class loader is: " + clazz.getClassLoader());
}
}
在Windows上使用JDK1.5输出如下:
class loader is: sun.misc.Launcher$AppClassLoader@82ba41
class loader is: sun.misc.Launcher$ExtClassLoader@923e30
thread context class loader is: sun.misc.Launcher$AppClassLoader@82ba41
system class loader is: sun.misc.Launcher$AppClassLoader@82ba41
bootstrap class loader is: null
由此可见,在Java进程启动时,main thread的Context Class Loader即为System Class Loader。
参见:
http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html