一 点睛
JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)。
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是 Java 虚拟机规范却没有这么定义,而是将所有派生于抽象类 ClassLoader 的类加载器都划分为自定义类加载器。无论类加载器的类型如何划分,在程序中我们最常见的类加载器结构主要是如下情况:
-
除了顶层的启动类加载器外,其余的类加载器都应当有自己的“父类”加戟器。
-
不同类加载器看似是继承(Inheritance)关系,实际上是包含关系。在下层加载器中,包含着上层加载器的引用。
父类加载器和子类加载器的关系如下:
class ClassLoader {
ClassLoader parent;//父类加载器
public ClassLoader(ClassLoader parent) {
this.parent = parent;
}
}
class ParentClassLoader extends ClassLoader {
public ParentClassLoader(ClassLoader parent) {
super(parent);
}
}
class ChildClassLoader extends ClassLoader {
public ChildClassLoader(ClassLoader parent) { //parent = new ParentClassLoader();
super(parent);
}
}
正是由于子类加载器中包含着父类加载器的引用,所以可以通过子类加载器的方法获取对应的父类加载器。
注意
启动类加载器通过 C/C++ 语言编写,而自定义类加载器都是由 Java 语言编写的,虽然扩展类加载器和应用程序类加载器是被 jdk 开发人员使用 java 语言来编写的,但是也是由 java 语言编写的,所以也被称为自定义类加载器。
二 引导类加载器
1 说明
启动类加载器(引导类加载器,Bootstrap ClassLoader)
-
这个类加载使用 C/C++ 语言实现的,嵌套在 JVM 内部。
-
它用来加载Java的核心库(JAVAHOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容)。用于提供 JVM 自身需要的类。
-
并不继承自 java.lang.ClassLoader,没有父加载器。
-
出于安全考虑,Bootstrap 启动类加载器只加载包名为 java、javax、sun 等开头的类
-
加载扩展类和应用程序类加载器,并指定为他们的父类加载器。
-
使用-XX:+TraceClassLoading参数得到。
启动类加载器使用 C++ 编写的。
C/C++:指针函数&函数指针、C++支持多继承、更加高效
Java:由 C++ 演变而来,(C++)–版,单继承
2 代码
package chapter04.java;
import java.net.URL;
/**
* 加载器测试
*/
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println("**********启动类加载器**************");
// 获取 BootstrapClassLoader 能够加载的 api 的路径
URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (URL element : urLs) {
System.out.println(element.toExternalForm());
}
// 从上面的路径中随意选择一个类,来看看他的类加载器是什么:引导类加载器
ClassLoader classLoader = java.security.Provider.class.getClassLoader();
System.out.println(classLoader);// null 引导类加载器是获取不到的
}
}
3 执行结果
**********启动类加载器**************
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/lib/resources.jar
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/lib/rt.jar
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/lib/sunrsasign.jar
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/lib/jsse.jar
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/lib/jce.jar
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/lib/charsets.jar
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/lib/jfr.jar
file:/D:/ProgramFiles/Java/jdk1.8.0_251/jre/classes
null
三 扩展类加载器
1 说明
扩展类加载器(Extension ClassLoader)
-
Java语言编写,由 sun.misc.Launcher$ExtClassLoader 实现。
-
继承于 ClassLoader 类
-
父类加载器为启动类加载器
从 java.ext.dirs 系统属性所指定的目录中加载类库,或从JDK的安装目录的 jre/lib/ext 子目录下加载类库。如果用户创建的 JAR 放在此目录下,也会自动由扩展类加载器加载。
2 代码
package chapter04.java;
import java.net.URL;
/**
* 加载器测试
*/
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println("***********扩展类加载器加载路径*************");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")) {
System.out.println(path);
}
// 从上面的路径中随意选择一个类,来看看他的类加载器是什么:扩展类加载器
ClassLoader classLoader1 = sun.security.ec.CurveDB.class.getClassLoader();
System.out.println(classLoader1); // sun.misc.Launcher$ExtClassLoader@1540e19d
}
}
3 执行结果
***********扩展类加载器加载路径*************
D:\ProgramFiles\Java\jdk1.8.0_251\jre\lib\ext
C:\WINDOWS\Sun\Java\lib\ext
sun.misc.Launcher$ExtClassLoader@5cad8086
Process finished with exit code 0
四 系统类加载器
应用程序类加载器(系统类加载器,AppClassLoader)
-
java 语言编写,由 sun.misc.Launcher$AppClassLoader 实现
-
继承于 ClassLoader 类
-
父类加载器为扩展类加载器
-
它负责加载环境变量 classpath 或系统属性 java.class.path 指定路径下的类库
-
应用程序中的类加载器默认是系统类加载器。
-
它是用户自定义类加载器的默认父加载器
-
通过 ClassLoader的getSystemClassLoader() 方法可以获取到该类加载器
五 用户自定义类加载器
用户自定义类加载器
-
在 Java 的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的。在必要时,我们还可以自定义类加载器,来定制类的加载方式。
-
体现 Java 语言强大生命力和巨大魅力的关键因素之一便是,Java 开发者可以自定义类加载器来实现类库的动态加载,加载源可以是本地的 JAR 包,也可以是网络上的远程资源。
-
通过类加载器可以实现非常绝妙的插件机制,这方面的实际应用案例举不胜举。例如,著名的 OSGI 组件框架,再如 Eclipse 的插件机制。类加载器为应用程序提供了一种动态增加新功能的机制,这种机制无须重新打包发布应用程序就能实现。
-
同时,自定义加载器能够实现应用隔离,例如 Tomcat,Spring 等中间件和组件框架都在内部实现了自定义的加载器,并通过自定义加载器隔离不同的组件模块。这种机制比 C/C++ 程序要好太多,想不修改 C/C++ 程序就能为其新增功能,几乎是不可能的,仅仅一个兼容性便能阻挡住所有美好的设想。
-
自定义类加载器通常需要继承于 ClassLoader。