Java ClassLoader 学习笔记(一)

Java ClassLoader 的体系结构是 Java 沙箱中的第一道安全防线。在运行时,一个类或接口不仅仅由它的类名来决定,而是由它的全限定性类名和它的定义类加载器共同决定的。每一类或接口仅属于一个运行时包(the runtime package)。一个类或接口的运行时包是由该类的包名和它的定义类加载器共同决定的。每一个运行时包所属的运行时命名空间是由它的定义类加载决定的。每一个被加载到 JVM 中的类在运行时只属于一个运行时命名空间。不可能向一个命名空间中加载两个全限定性类名完全相同但内容不同的类。注意:这里说的命名空间不同于 Java 类的命名空间(包路径)。

类加载器的架构

JVM 启动时使用 bootstrap class loader 创建一个启动类(an initial class),然后调用该类的 main 方法来驱动整个应用程序的执行。默认情况下,JVM 提供了三个类加载器:

1. Bootstrap classes:the runtime classes in rt.jar, internationalization classes in i18n.jar, and others.

2. Installed extensions:classes in JAR files in the lib/ext directory of the JRE, and in the system-wide, platform-specific extension directory (such as /usr/jdk/packages/lib/ext on the Solaris™ Operating System, but note that use of this directory applies only to Java™ 6 and later).

3. The class path:classes, including classes in JAR files, on paths specified by the system property java.class.path. If a JAR file on the class path has a manifest with the Class-Path attribute, JAR files specified by the Class-Path attribute will be searched also. By default, the java.class.path property's value is ., the current directory. You can change the value by using the -classpath or -cp command-line options, or setting the CLASSPATH environment variable. The command-line options override the setting of the CLASSPATH environment variable.

它们的父子关系如下图所示:

Java ClassLoader 学习笔记(一)_classloader

其中,浅灰色背景的是用户自定义类加载器。如果你在创建自定义类加载器时未显示指定其父类加载器,那么 JVM 的 system class loader(即 class path class loader) 将被指定为其父类加载器。再来体验一个复杂一点的加载,如果类 D 通过其运行时常量池引用了类 C,那么

1. 如果类 D 是由 bootstrap class loader 定义的,那么 bootstrap class loader 就会发起类 C 的加载

2. 如果类 D 是由一个 user-defined class loader 定义的,那么这个 user-defined class loader 就会发起类 C 的加载

如何加载类

Java 平台使用一个委托模型来加载类。除了引导类加载器(Bootstrap Class Loader)之外,其他每一个类加载都有一个父类加载器,因为引导类加载器是类加载树形结构的根。当你请求一个类加载器加载指定的类时,它会先找找自己是不是亲自加载过这个类,如果是的,则直接返回为该类创建的 Class 对象,否则,将委托它的父类加载器去加载该类。其父类加载器同样也会先找找自己是不是亲自加载过这个类,如果是,则直接返回该类对应的 Class 对象,否则,再次委托它的父类加载器去加载该类。如此一致向上委托下去,直到加载成功或引导类加载器也加载失败。此时,如果所有的父类加载器都加载失败了, 那么起初接受这个请求的类加载器就会担负起加载该类的责任。ClassLoader 类中实现类加载的源码片段:

protectedsynchronizedClass<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}

if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}

findLoadedClass(String) 方法:如果当前类加载器被 JVM 记录为请求加载的类的初始类加载器(initiating class loader),那么就返回为该类创建的 Class 对象,否则返回 null。那么什么是初始类加载器(initiating class loader)呢?还是来扯扯相关的几个概念吧。假定类加载器 L 通过直接定义它(指类C)或委托给另一个类加载器而创建了类 C,那么如果类加载器 L 直接创建了类 C,我们就说类加载器 L 定义了类 C,或者说类加载器 L 是类 C 的定义类加载器(the defining loader);如果类加载器 L 是通过委托给另一个类加载器而创建了类 C,那么我们就说类加载器 L 发起了类 C 的加载,或者说类加载器 L 是类 C 的初始类加载器(the initiating loader)。

findClass(String) 方法:如果当前类加载器的所有父类加载器都未能成功加载指定类,那么它就调用该方法,亲自去搜索查找对应的 .class 文件,加载该文件,并创建这个类。

参考资源:

1.http://download.oracle.com/javase/tutorial/ext/basics/load.html

2.http://download.oracle.com/javase/tutorial/deployment/jar/apiindex.html

3.http://onjava.com/pub/a/onjava/2005/01/26/classloading.html?page=1

4.JVM Specification Chapter 5.2 - 5.3

5.http://www.developer.com/java/other/article.php/10936_2248831_2/Java-Class-Loading-The-Basics.htm