2009年12月12日星期六

[精品][原创]Java ClassLoader总结

Classloader是Java语言用于载入.class文件。.class文件是包含了java字节码。
Classloader通过loadClass方法载入class文件,得到Class类。再用newInstance();方法获得类的实例。

委托模式:
Java1.2之后,Classloader添加了委托模式,当载入一个class文件时,
  • 首先查找是否已经载入了此类;
  • 如果没有则会先调用它的getParent()方法得父类的Classloader,然后让父类载入class文件;
  • 如果父类载入失败的话那么会自己本身载入class,如果再载入失败则抛出ClassNotFoundException。
这是为了安全考虑,如果我们建了一个包名都一样的String类,那么不使用委托模式会导致不安全。
另外委托模式对自定义的Classloader不起作用。除非自己实现。

使用ClassLoader方法:
  • 方法一:
MyClassLoader cl = new MyClassLoader();
cl.loadClass("test.classloader.A");方法直接使用MyClassLoader载入类。
  • 方法二:
Class.forName("test.classloader.A",true,new MyClassLoader());方法使用MyClassLoader载入类。
  • 方法三:
Class.forName("test.classloader.A");方法使用CallerClassLoader载入类。
new test.classloader.A();方法使用CallerClassLoader载入类。

Caller Classloader指的是当前所在的类装载时使用的Classloader,它可能是System Classloader,也可能是一个自定义的Classloader,这里,我们都称之为Caller Classloader。我们可以通过getClass().getClassLoader()来得到Caller Classloader。例如,存在A类,是被AClassLoader所加载,A.class.getClassLoader()为AClassLoader的实例,它就是A.class的Caller Classloader。

如果在A类中使用new关键字,或者Class.forName(String className)和Class.getResource(String resourceName)方法,那么这时也是使用Caller Classloader来装载类和资源。比如在A类中初始化B类:
/**
* A.java
*/
public void foo() {
B b = new B();
b.setName("b");
}

那么,B类由当前Classloader,也就是AClassloader装载。同样的,修改上述的foo方法,其实现改为:

Class clazz = Class.forName("foo.B");

最终获取到的clazz,也是由AClassLoader所装载。

  • 方法四:
使用Thread的ClassLoader来载入类。
Thread.currentThread().setContextClassLoader(new MyClassLoader());
Thread.currentThread().getContextClassLoader().loadClass("test.classloader.A");

系统默认的三种ClassLoader

(Bootstrap) 装载器:该装载器没有父装载器,它是 JVM 实现的一部分,从 sun.boot.class.path 装载运行时库的核心代码。

扩展 (Extension) 装载器:继承的父装载器为根装载器,不像根装载器可能与运行时的操作系统有关,这个类装载器是用纯 Java 代码实现的,它从 java.ext.dirs ( 扩展目录 ) 中装载代码。

系统 (System or Application) 装载器:装载器为扩展装载器,我们都知道在安装 JDK 的时候要设置环境变量 (CLASSPATH ) ,这个类装载器就是从 java.class.path(CLASSPATH 环境变量 ) 中装载代码的,它也是用纯 Java 代码实现的,同时还是用户自定义类装载器的缺省父装载器。


ClassLoader中重要方法:
  1. loadCass方法:loadClass(String name ,boolean resolve) 其中 name 参数指定了 JVM 需要的类的名称 , 该名称以类的全限定名表示,如 Java.lang.Object ; resolve 参数告诉方法是否需要解析类,在初始化类之前,应考虑类解析,并不是所有的类都需要解析,如果 JVM 只需要知道该类是否存在或找出该类的超类,那么就不需要解析。这个方法是 ClassLoader 的入口点。
  2. defineClass方法:这个方法接受类文件的字节数组并把它转换成 Class 对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。
  3. findSystemClass方法:findSystemClass 方法从本地文件系统装入 Java 字节码。它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将字节数组转换成 Class 对象。当运行 Java 应用程序时 , 这是 JVM 正常装入类的缺省机制。
  4. resolveClass方法resolveClass(Class c):方法解析装入的类,如果该类已经被解析过那么将不做处理。当调用 loadClass 方法时 , 通过它的 resolve 参数决定是否要进行解析。
  5. findLoadedClass方法:当调用 loadClass 方法装入类时 , 调用 findLoadedClass 方法来查看 ClassLoader 是否已装入这个类 , 如果已装入 , 那么返回 Class 对象 , 否则返回 NULL 。如果强行装载已存在的类 , 将会抛出链接错误。

没有评论: