9.2 类加载器的层次结构与代理模式

类加载器的一个重要特征是所有类加载器对象都可以有一个作为其双亲的类加载器对象。通过ClassLoader类的getParent方法可以获取双亲类加载器对象。ClassLoader类提供的构造方法允许在创建时指定类加载器对象的双亲类加载器对象。不过ClassLoader类本身是抽象的,无法直接创建出ClassLoader类本身的对象。自定义类加载器的Java类继承自ClassLoader类。在自定义类加载器的构造方法中应该调用父类ClassLoader类的构造方法,并指定双亲类加载器对象的值。如果在创建时不指定双亲类加载器,则默认双亲类加载器是系统类加载器。如果当前类加载器对象的双亲类加载器是启动类加载器,那么有些虚拟机实现会选择让getParent方法返回null,从而无法获取启动类加载器的对象引用。虚拟机中的类加载器对象按照这种方式组织起来,形成一个树状层次结构。如果程序中大量使用自定义类加载器,这个层次结构会比较复杂。类加载器也可以选择没有双亲类加载器。

如果不使用自定义类加载器,则一个Java程序运行时的类加载器通常有3个层次,由之前介绍过的Java平台上默认提供的类加载器形成,从根节点开始依次是启动类加载器、扩展类加载器和系统类加载器。代码清单9-2给出了查看这些类加载器的层次结构的示例。

代码清单9-2 查看类加载器的层次结构的示例


public void displayParents(){

ClassLoader current=getClass().getClassLoader();

while(current!=null){

System.out.println(current.toString());

current=current.getParent();

}

}


在Java SE 7的OpenJDK实现中运行此代码,输出的信息如代码清单9-3所示。输出的第一个类加载器对象是加载应用程序的系统类加载器,由sun.misc.Launcher$AppClassLoader类表示。输出的第二个类加载器对象是扩展类加载器,由sun.misc.Launcher$ExtClassLoader类表示,它是系统类加载器的双亲类加载器。由于扩展类加载器的双亲类加载器是启动类加载器,getParent方法返回为null,因此在代码清单9-3中没有输出。

代码清单9-3 查看类加载器的层次结构的输出结果


sun.misc.Launcher$AppClassLoader@177b3cd

sun.misc.Launcher$ExtClassLoader@1bd7848


类加载器在加载Java类时通常使用代理模式。代理模式指的是一个类加载器对象既可以自己完成Java类的定义工作,也可以代理给其他的类加载器对象来完成。在ClassLoader类的默认实现中,当类加载器对象需要加载一个Java类或资源时,会先把加载请求代理给双亲类加载器对象来完成。只有在双亲类加载器对象无法找到Java类或资源时,才由当前类加载器对象进行处理。这种代理关系会沿着类加载器对象的层次结构树一直向上传递,直到成功加载一个类。在加载类的过程中,依靠双亲类加载器对象的原因是有些类的加载只有双亲类加载器对象才能完成。在程序运行过程中,从类加载器层次结构树的根节点开始,不断有新的类加载器对象被添加进来。对于之后添加的类加载器对象来说,加载类时所需的一些信息对当前类加载器对象来说是不可见的。对于这样的类的加载,只能代理给双亲类加载器对象来完成。不过当前类加载器对象可以选择在不同的时机把加载请求代理给双亲类加载器对象。ClassLoader类的默认实现使用的是双亲优先的策略,即先由双亲类加载器对象尝试加载,找不到的情况下再由当前类加载器对象来尝试加载。程序可以根据需要采用当前类加载器优先的策略,即先由当前类加载器尝试加载,找不到的情况下再代理给双亲类加载器对象尝试加载;或者根据要加载的Java类的名称采取不同的查找策略。

在代码清单9-4中,NoParentClassLoader类在构造方法中设置双亲类加载器为null,在testLoad方法中尝试加载当前包中的一个Java类,由于没有双亲类加载器对象可以代理加载类的请求,因此加载过程失败。

代码清单9-4 没有设置双亲类加载器对象的类加载器


public class NoParentClassLoader extends ClassLoader{

public NoParentClassLoader(){

super(null);

}

public void testLoad()throws ClassNotFoundException{

Class<?>clazz=loadClass("com.java7book.chapter9.ToLoad");

}

}