Java基础—ClassLoader的理解

标签: Java | 发表时间:2014-09-02 22:40 | 作者:zhanjindong
出处:http://zhanjindong.info
##默认的三个类加载器
Java默认是有三个ClassLoader,按层次关系从上到下依次是:

 - Bootstarp ClassLoader
 - Ext ClassLoader
 - System ClassLoader

Bootstrap ClassLoader是最顶层的ClassLoader,它比较特殊,是用C++编写集成在JVM中的,是JVM启动的时候用来加载一些核心类的,比如:`rt.jar`,`resources.jar`,`charsets.jar`,`jce.jar`等,可以运行下面代码看都有哪些:
```
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();  
		for (int i = 0; i < urls.length; i++) {  
		    System.out.println(urls[i].toExternalForm());  
		} 
```
其余两个ClassLoader都是继承自`ClassLoader`这个类。Java的类加载采用了一种叫做“双亲委托”的方式(稍后解释),所以除了`Bootstarp ClassLoader`其余的ClassLoader都有一个“父”类加载器, 不是通过集成,而是一种包含的关系。
``` 
//ClassLoader.java
public abstract class ClassLoader {
    ...
    // The parent class loader for delegation
    private ClassLoader parent;
    ...
```
##“双亲委托”
所谓“双亲委托”就是当加载一个类的时候会先委托给父类加载器去加载,当父类加载器无法加载的时候再尝试自己去加载,所以整个类的加载是“自上而下”的,如果都没有加载到则抛出`ClassNotFoundException`异常。

上面提到Bootstarp ClassLoader是最顶层的类加载器,实际上Ext ClassLoader和System ClassLoader就是一开始被它加载的。

ClassLoader称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar(包括自己手动放进去的jar包)。

System ClassLoader叫做系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件,包括我们平时运行jar包指定cp参数下的jar包。

运行下面的代码可以验证上面内容:
```
ClassLoader loader = Debug.class.getClassLoader();    
		while(loader != null) {  
		    System.out.println(loader);  
		    loader = loader.getParent();    
		}  
		System.out.println(loader);
```

##“双亲委托”的作用
之所以采用“双亲委托”这种方式主要是为了安全性,避免用户自己编写的类动态替换Java的一些核心类,比如String,同时也避免了重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader加载就是不同的两个类,如果相互转型的话会抛`java.lang.ClassCaseException`.

##自定义类加载器
除了上面说的三种默认的类加载器,用户可以通过继承`ClassLoader`类来创建自定义的类加载器,之所以需要自定义类加载器是因为有时候我们需要通过一些特殊的途径创建类,比如网络。

至于自定义类加载器是如何发挥作用的,`ClassLoader`类的loadClass方法已经把算法定义了:
```
protected synchronized Class<?> 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;
    }
```

>1. Invoke `findLoadedClass(String)` to check if the class has already been loaded. 

>2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead. 

>3. Invoke the `findClass(String)` method to find the class. 

看上面的Javadoc可以知道,自定义的类加载器只要重载`findClass`就好了。
##Context ClassLoader
首先Java中ClassLoader就上面提到的四种,`Bootstrap ClassLoader`,`Ext ClassLoader`,`System ClassLoader`以及用户自定义的,所以`Context ClassLoader`并不是一种新的类加载器,肯定是这四种的一种。

首先关于类的加载补充一点就是如果类A是被一个加载器加载的,那么类A中引用的B也是由这个加载器加载的(如果B还没有被加载的话),通常情况下就是类B必须在类A的classpath下。

但是考虑多线程环境下不同的对象可能是由不同的ClassLoader加载的,那么当一个由ClassLoaderC加载的对象A从一个线程被传到另一个线程ThreadB中,而ThreadB是由ClassLoaderD加载的,这时候如果A想获取除了自己的classpath以外的资源的话,它就可以通过`Thread.currentThread().getContextClassLoader()`来获取线程上下文的ClassLoader了,一般就是ClassLoaderD了,可以通过`Thread.currentThread().setContextClassLoader(ClassLoader)`来显示的设置。

##为什么要有Contex ClassLoader
之所以有Context ClassLoader是因为Java的这种“双亲委托”机制是有局限性的:

 - 举网上的一个例子:
 

> JNDI为例,JNDI的类是由bootstarp ClassLoader从rt.jar中间载入的,但是JNDI具体的核心驱动是由正式的实现提供的,并且通常会处于-cp参数之下(注:也就是默认的System ClassLoader管理),这就要求bootstartp ClassLoader去载入只有SystemClassLoader可见的类,正常的逻辑就没办法处理。怎么办呢?parent可以通过获得当前调用Thread的方法获得调用线程的>Context ClassLoder 来载入类。

 - 我上面提到的加载资源的例子。
 

`Contex ClassLoader`提供了一个突破这种机制的后门。

Context ClassLoader一般在一些框架代码中用的比较多,平时写代码的时候用类的ClassLoader就可以了。

##参考链接
[http://stackoverflow.com/questions/1771679/difference-between-threads-context-class-loader-and-normal-classloader][1]
 


  [1]: http://stackoverflow.com/questions/1771679/difference-between-threads-context-class-loader-and-normal-classloader

相关 [java 基础 classloader] 推荐:

Java基础—ClassLoader的理解

- - ZJD'S NOTES
} ``` 其余两个ClassLoader都是继承自`ClassLoader`这个类. Java的类加载采用了一种叫做“双亲委托”的方式(稍后解释),所以除了`Bootstarp ClassLoader`其余的ClassLoader都有一个“父”类加载器, 不是通过集成,而是一种包含的关系. ``` ##“双亲委托” 所谓“双亲委托”就是当加载一个类的时候会先委托给父类加载器去加载,当父类加载器无法加载的时候再尝试自己去加载,所以整个类的加载是“自上而下”的,如果都没有加载到则抛出`ClassNotFoundException`异常.

java之classloader体系结构

- - 非技术 - ITeye博客
原文出处: http://www.iteye.com/topic/136427. a, Bootstrap ClassLoader/启动类加载器. 主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作.. b, Extension ClassLoader/扩展类加载器.

Java ClassLoader原理分析

- - Java - 编程语言 - ITeye博客
一、JDK默认提供的三个ClassLoader. JDK 默认提供了如下几种ClassLoader. Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载 %JAVA_HOME%/jre/lib, -Xbootclasspath参数指定的路径以及 %JAVA_HOME%/jre/classes中的类.

Java类加载器classLoader的工作机制

- - CSDN博客推荐文章
类加载器就是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件. 在Java中,类转载器把一个类装入JVM中,需要经过以下步骤:. 1.装载:查找和导入Class文件. 2.链接: 执行校验、准备和解析步骤,其中解析步骤是可以选择的:. a)校验: 检查载入Class文件数据的正确性;. b)准备:给类的静态变量分配存储空间;.

java基础知识

- - CSDN博客互联网推荐文章
JAVA相关基础知识. 1、面向对象的特征有哪些方面. 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面. 抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节. 抽象包括两个方面,一是过程抽象,二是数据抽象. 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.

JAVA面试精选【Java基础】

- - CSDN博客编程语言推荐文章
  这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱. 只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了. 对于那些正打算找工作JAVA软件开发工作的童鞋们来说,当你看到这份题目的时候,你应该感动很幸运,因为,只要你把题目中的内容都搞懂了,在笔试的时候就可以游刃有余,通过面试只有半步之遥了,笔试只能反映你的JAVA技能.

Java并发编程基础

- - 并发编程网 - ifeve.com
并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力. 如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善. 现代的PC都有多个CPU或一个CPU中有多个核. 是否能合理运用多核的能力将成为一个大规模应用程序的关键. 进程是以独立于其他进程的方式运行的,进程间是互相隔离的.

深入浅出ClassLoader(译)

- - 并发编程网 - ifeve.com
你真的了解ClassLoader吗. 这篇文章翻译自zeroturnaround.com的 . Do You Really Get Classloaders? ,融入和补充了笔者的一些实践、经验和样例. 本文的例子比原文更加具有实际意义,文字内容也更充沛一些,非常感谢作者  Jevgeni Kabanov 能够共享如此优秀的文档.

JAVA基础之理解JNI原理

- shuangxi - 博客园-首页原创精华区
JNI是JAVA标准平台中的一个重要功能,它弥补了JAVA的与平台无关这一重大优点的不足,在JAVA实现跨平台的同时,也能与其它语言(如C、C++)的动态库进行交互,给其它语言发挥优势的机会. 有了JAVA标准平台的支持,使JNI模式更加易于实现和使用. 环境说明:ubuntu 10.4.2 LTS系统.