【Java ClassLoader高级应用】:2大类加载器,深入理解URLClassLoader和ExtensionClassLoader
发布时间: 2024-09-25 06:13:43 阅读量: 68 订阅数: 27
![【Java ClassLoader高级应用】:2大类加载器,深入理解URLClassLoader和ExtensionClassLoader](https://img-blog.csdnimg.cn/img_convert/56d9aa90d4ee92f53855a3b423a8127e.png)
# 1. Java ClassLoader概述
## 1.1 ClassLoader简介
ClassLoader是Java语言中的类加载器,它是Java运行时环境的一部分,负责从文件系统、网络或其他来源加载Class文件到JVM内存中,供JVM调用。它使得Java具有了动态加载类的能力,从而能够支持插件架构等复杂的应用场景。
## 1.2 ClassLoader的作用
ClassLoader的作用不仅限于加载类文件,还包括类的链接、初始化。它保证了Java应用的模块化和安全性,因为类加载器可以按照不同的层次结构进行加载,例如:不同应用可以使用完全不同的类加载器,从而保证了相互之间的隔离。
## 1.3 ClassLoader的层次结构
Java中的ClassLoader具有层次结构,通常由最顶层的Bootstrap ClassLoader启动加载JRE的核心类库,然后是Extension ClassLoader负责加载扩展库,再由System ClassLoader加载应用的Class,最后是自定义的ClassLoader负责加载特定需求的类。这种层次结构确保了加载类的顺序性和安全性。
# 2. 深入分析ClassLoader机制
## 2.1 ClassLoader的基本概念
### 2.1.1 ClassLoader的职责和作用
ClassLoader在Java虚拟机(JVM)中扮演着至关重要的角色,它的主要职责是动态加载类。当JVM启动时,并不是一次性加载所有的类,而是在需要时才加载对应的类,这使得Java应用程序能够更加轻量级和模块化。
ClassLoader的主要作用包括:
- **动态加载**:在运行时刻动态地加载类文件到内存中。
- **按需加载**:仅在真正需要的时候加载类,而不是程序启动时就加载全部类。
- **封装性**:隔离不同来源的类,如本地文件系统、网络等,确保类加载安全。
- **扩展性**:通过继承ClassLoader类,开发者可以定义自己的类加载逻辑。
### 2.1.2 ClassLoader的继承体系
ClassLoader体系结构是层次化的,它主要由Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader(又称为Application ClassLoader)以及用户自定义的ClassLoader组成。
- **Bootstrap ClassLoader**:也称为引导类加载器,它负责加载JRE的核心库,通常以本地代码实现,并且不继承自java.lang.ClassLoader。
- **Extension ClassLoader**:负责加载JRE扩展目录下的类库,通常是由sun.misc.Launcher$ExtClassLoader实现。
- **System ClassLoader**:负责加载CLASSPATH环境变量中指定的类库,实现类为sun.misc.Launcher$AppClassLoader。
- **自定义ClassLoader**:应用程序可以通过继承java.lang.ClassLoader类来创建自定义的类加载器。
## 2.2 ClassLoader的加载过程
### 2.2.1 类的加载时机和双亲委派模型
类加载通常在以下几种情况下发生:
- 创建类的实例时。
- 访问类的静态变量或静态方法时。
- 使用反射对类进行操作时。
- 子类被加载时。
- 调用Class.forName()方法时。
Java采用的是双亲委派模型,当一个ClassLoader尝试加载一个类时,它首先会把这个类加载请求委派给父ClassLoader去完成,从顶层的Bootstrap ClassLoader开始,一直到最底层的ClassLoader。这种方式能够确保核心Java类库的安全加载,避免用户自定义的类覆盖了Java标准类库。
### 2.2.2 加载类的步骤和流程
类加载器加载类通常会经历以下步骤:
1. **加载**:根据类名读取类文件,转化为字节码流。
2. **链接**:将字节码流转化为方法区的内部数据结构,进行验证、准备和解析。
3. **初始化**:为类变量赋初始值,执行静态代码块。
ClassLoader加载类的具体实现流程如下:
```java
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 首先,检查请求的类是否已经被加载过
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 如果父类加载器存在,则优先委派给父类加载器
c = parent.loadClass(name, false);
} else {
// Bootstrap ClassLoader
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 父类加载器无法加载时抛出异常,不影响本类加载器处理
}
if (c == null) {
// 如果仍然没有找到,则调用findClass方法来查找
long t1 = System.nanoTime();
c = findClass(name);
// 记录加载类的时间
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
```
- `findLoadedClass`方法用于检查指定名称的类是否已经被加载过。
- 如果没有被加载过,会先尝试通过`parent.loadClass`(委派给父加载器)加载类。
- 如果父加载器加载失败,调用`findClass`方法来查找类。
## 2.3 自定义ClassLoader
### 2.3.1 创建自定义ClassLoader的动机和场景
在某些特定的场景下,开发者可能需要创建自己的ClassLoader来满足特定的需求。以下是一些自定义ClassLoader的常见动机和场景:
- **热部署**:在不重启服务器的情况下更新应用程序的类文件。
- **加密/解密**:需要对字节码进行加密和解密处理的场景。
- **模块化开发**:应用可能需要在运行时动态加载不同的模块。
### 2.3.2 实现自定义ClassLoader的步骤
实现自定义ClassLoader的步骤通常包括:
1. 继承java.lang.ClassLoader类。
2. 重写findClass方法来定义加载逻辑。
3. 通过defineClass方法将字节码转换为Class对象。
下面是一个简单的自定义ClassLoader实现示例:
```java
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = loadClassData(name);
if (data == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, data, 0, data.length);
}
}
private byte[] loadClassData(String className) {
// 将类名转换为文件路径
String path = classPath + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
try {
// 打开文件
FileInputStream fis = new FileInputStream(path);
// 获取文件长度
int size = fis.available();
byte[] buff = new byte[size];
fis.read(buff);
fis.close();
return buff;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
```
在上述代码中,`MyClassLoader`类继承了`ClassLoader`类,并重写了`findClass`方法。`loadClassData`方法用于从文件系统中加载类的字节码数据。当需要加载一个类时,`MyClassLoader`会首先尝试调用`findLoadedClass`方法检查该类是否已经被加载,如果没有,则调用`findClass`方法来查找类,最后通过`defineClass`将字节码转换为`Class`对象。
通过上面的分析和示例,我们可以看到ClassLoader的机制非常灵活,它既可以通过继承扩展新的加载方式,
0
0