ClassLoader的双亲委派机制

java 是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是:

  1. 类加载器;
  2. .class 文件检验器;
  3. 内置于java虚拟机的安全特性;
  4. 安全管理器及java API;

类的加载体系:
java 程序中的 .java 文件编译完会生成 .class 文件,而 .class 文件就是通过被称为类加载器的 ClassLoader 加载的,ClassLoder 在加载过程中会使用“双亲委派机制”来加载 .class 文件,如图:
ClassLoader的双亲委派机制

BootStrapClassLoader:启动类加载器,Jvm启动时创建,用 于 加载$JAVA_HOME$/jre/lib 下面的类库,由于启动类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不能直接通过引用进行操作。

ExtClassLoader:扩展类加载器,ExtClassLoader 会加载 $JAVA_HOME/jre/lib/ext 下的类库;

AppClassLoader:应用程序类加载器,AppClassLoader 会加载 java 环境变量
CLASSPATH 所 指 定 的 路 径 下 的 类 库 , 而 CLASSPATH 所 指 定 的 路 径 可 以 通 过System.getProperty(“java.class.path”)获取;当然,该变量也可以覆盖,可以使用参数-cp,例如: java -cp 路径 (可以指定要执行的 class 目录);

CustomClassLoader:自定义类加载器,该 ClassLoader 是指我们自定义的 ClassLoader ,大部分情况下使用 AppClassLoader 就足够了。

! ClassLoader 的双亲委派机制

  1. 当 AppClassLoader 加载一个 class 时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器 ExtClassLoader 去完成;
  2. 当 ExtClassLoader 加载一个 class 时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader 去完成;
  3. 如果 BootStrapClassLoader 加载失败(例如在JAVAHOMEJAVA_HOME/jre/lib 里未查找到该 class),会使用ExtClassLoader 来尝试加载;
  4. 若 ExtClassLoader 也加载失败,则会使用 AppClassLoader 来加载,如果 AppClassLoader 也加载失败,则会报出异常 ClassNotFoundException。

下面贴下 ClassLoader 的 loadClass(String name, boolean resolve)的源码:

 protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
	 // 首先找缓存是否有 class
	 Class c = findLoadedClass(name);
	 if (c == null) {
	 //没有判断有没有父类
		try {
			 if (parent != null) {
				 //有的话,用父类递归获取 class
				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) {
		 // 如果还是没有找到,调用 findClass(name)去找这个类
		 c = findClass(name);
		 }
	 }
	 
	 if (resolve) {
		resolveClass(c);
	}
	 return c;
 }

代码很明朗:首先找缓存(findLoadedClass),没有的话就判断有没有 parent,有的话就用 parent 来递归的 loadClass,然而 ExtClassLoader 并没有设置 parent,则会通过 findBootstrapClassOrNull 来加载 class,而findBootstrapClassOrNull 则会通过 JNI 方法” private native Class findBootstrapClass(String name)“来使用 BootStrapClassLoader 来加载 class。然后如果 parent 未找到 class,则会调用 findClass 来加载 class, findClass 是一个 protected 的空方法,可以覆盖它以便自定义 class 加载过程。
另 外 , 虽 然 ClassLoader 加 载 类 是 使 用 loadClass 方 法 , 但 是 鼓 励 用 ClassLoader 的 子 类 重写findClass(String),而不是重写 loadClass,这样就不会覆盖了类加载默认的双亲委派机制。

上图助于理解:
ClassLoader的双亲委派机制

好处:
这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,但是在这种机制下这些系统的类已经被Bootstrap classLoader加载过了,所以并不会再去加载,从一定程度上防止了危险代码的植入。