类装载器学习笔记
JAVA的类装载器(ClassLoader)
Java 类加载器
深入探讨 Java 类加载器
Java类加载器浅析
java类加载器体系结构(含hotswap原理)
Java类加载器浅析
http://www.4ucode.com/Study/Topic/540714
http://zeroliu.iteye.com/blog/29541
类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java Applet 需要从远程下载 Java 类文件到浏览器中并执行。现在类加载器在 Web 容器和 OSGi 中得到了广泛的使用。一般来说,Java 应用的开发人员不需要直接同类加载器进行交互。Java 虚拟机默认的行为就已经足够满足大多数情况的需求了。不过如果遇到了需要与类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就很容易花大量的时间去调试 ClassNotFoundException和 NoClassDefFoundError等异常。本文将详细介绍 Java 的类加载器,帮助读者深刻理解 Java 语言中的这个重要概念。下面首先介绍一些相关的基本概念。
Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。系统提供的类加载器主要有下面三个:
类加载器也是Java类,因为其它Java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这个就是BootStrap。BootStrap它是嵌套在Java虚拟机内核中的,jvm启动,这个类就会启动,它是由c++语言编写的。Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器(App ClassLoader)为其父级类加载。
/*LoaderSample1.java*/ public class LoaderSample1 { public static void main(String[] args) { Class c; ClassLoader cl; cl = ClassLoader.getSystemClassLoader();// 系统类装载器实例化 System.out.println(cl);//sun.misc.Launcher$AppClassLoader@40affc70 System.out.println("-----------------"); while (cl != null) { cl = cl.getParent();// parent实例化 System.out.println(cl);//sun.misc.Launcher$ExtClassLoader@61e63e3d和null } System.out.println("-----------------"); try { c = Class.forName("java.lang.Object"); cl = c.getClassLoader();//获取核心类java.lang.Object的类加载器 System.out.println(c.getName()+":Classloader is " + cl); c = Class.forName("LoaderSample1"); cl = c.getClassLoader();//获取用户类LoaderSample1的类加载器 System.out.println(c.getName()+":loader is " + cl); } catch (Exception e) { e.printStackTrace(); } } }
sun.misc.Launcher$AppClassLoader@40affc70 ----------------- sun.misc.Launcher$ExtClassLoader@61e63e3d null ----------------- java.lang.Object:Classloader is null LoaderSample1:loader is sun.misc.Launcher$AppClassLoader@40affc70
第1行表示,系统类装载器实例化自类sun.misc.Launcher$AppClassLoader
第3行表示,系统类装载器的parent实例化自类sun.misc.Launcher$ExtClassLoader
第4行表示,系统类装载器parent的parent为bootstrap
第6行表示,核心类java.lang.Object是由bootstrap装载的
第7行表示,用户类LoaderSample1是由系统类装载器装载的
在Java中每个类都是由某个类加载器的实体来载入的,因此在Class类的实体中,都会有字段记录载入它的类加载器的实体(当为null时,其实是指Bootstrap ClassLoader)。 在java类加载器中除了引导类加载器(既Bootstrap ClassLoader),所有的类加载器都有一个父类加载器(因为他们本身自己就是java类)。而类的加载机制是遵循一种委托模式:当类加载器有加载类的需求时,会先请求其Parent加载(依次递归),如果在其父加载器树中都没有成功加载该类,则由当前类加载器加载。
Java的类加载器分为以下几种:
BootStrap -> ExtClassLoader -> AppClassLoader (即通常所说的System ClassLoader)
BootStrap------>JRE/lib/rt.jar ExtClassLoader---------->JRE/lib/ext/*.jar AppClassLoader---------->CLASSPATH指定的所有jar或目录。
补充:(ps:2011-12-4)
参考:http://blog.csdn.net/lovingprince/article/details/4239491
如 图2所示,loader2的parent为loader1,loader1的parent为system class loader。假设loader2被要求装载类MyClass,在parent delegation模型下,loader2首先请求loader1代为装载,loader1再请求系统类装载器去装载MyClass。若系统装载器能成功装载,则将MyClass所对应的Class对象的reference返回给loader1,loader1再将reference返回给 loader2,从而成功将类MyClass装载进虚拟机。若系统类装载器不能装载MyClass,loader1会尝试装载MyClass,若 loader1也不能成功装载,loader2会尝试装载。若所有的parent及loader2本身都不能装载,则装载失败。
若有一个能成功装载,实际装载的类装载器被称为定义类装载器,所有能成功返回Class对象的装载器(包括定义类装载器)被称为初始类装载器。如图1所示,假设loader1实际装载了MyClass,则loader1为MyClass的定义类装载器,loader2和loader1为MyClass的初始类装载器。
图 2 parent delegation模型
需要指出的是,Class Loader是对象,它的父子关系和类的父子关系没有任何关系。一对父子loader可能实例化自同一个Class,也可能不是,甚至父loader实例化自子类,子loader实例化自父类。假设MyClassLoader继承自ParentClassLoader,我们可以有如下父子loader:
ClassLoader loader1 = new MyClassLoader(); //参数 loader1 为 parent ClassLoader loader2 = new ParentClassLoader(loader1);那么parent delegation模型为什么更安全了? 因为在此模型下用户自定义的类装载器不可能装载应该由父亲装载器装载的可靠类,从而防止不可靠甚至恶意的代码代 替由父亲装载器装载的可靠代码。实际上,类装载器的编写者可以自由选择不用把请求委托给parent,但正如上所说,会带来安全的问题。
package uRLClassLoaderTest1; /*sub/Loadersample3.java*/ public class LoaderSample3 { public int age = 30; //静态代码块,类被装在的时候自动运行。 static { System.out.println("LoaderSample3 loaded"); System.out.println(LoaderSample3.class.getClassLoader());//输出类装载器的类型 } }
package uRLClassLoaderTest0; /*LoaderSample2.java*/ import java.net.*; import java.lang.reflect.*; public class LoaderSample2 { public static void main(String[] args) { try { //String path = System.getProperty("user.dir"); URL[] us = {new URL("file:d:/jarLoaderSample3.jar")}; ClassLoader loader = new URLClassLoader(us); Class c = loader.loadClass("uRLClassLoaderTest1.LoaderSample3"); System.out.println(LoaderSample2.class.getClassLoader());//输出类装载器的类型 Object o = c.newInstance(); Field f = c.getField("age"); int age = f.getInt(o); System.out.println("age is " + age); } catch (Exception e) { e.printStackTrace(); } } }
sun.misc.Launcher$AppClassLoader@40affc70 LoaderSample3 loaded java.net.URLClassLoader@544a5ab2 age is 30
从运行结果中可以看出,在类LoaderSample2中可以创建处于另一命名空间的类LoaderSample3中的对象并可以访问其公共成员age。并且LoaderSample2是由系统类装载器AppClassLoader装载,而LoaderSample3则是由URLClassLoader装载。