自定义类加载器
自定义类加载器在Java中是非常有用的,它们提供了许多高级功能和灵活性,使得开发者能够根据自己的需求定制类的加载行为。以下是为什么需要自定义类加载器的一些主要原因:
1. 隔离类库:
自定义类加载器允许开发者创建独立的类空间,从而隔离不同的类库。这对于某些插件式架构或模块化系统非常有用,其中每个插件或模块都有自己的类库和依赖项。通过使用不同的类加载器加载这些类库,可以避免版本冲突和依赖问题。
2. 热部署和动态更新:
自定义类加载器允许在不重启应用程序的情况下,重新加载和更新已加载的类。这对于需要频繁更新或替换代码的系统非常有用,如Web应用程序。通过使用自定义类加载器,可以动态地卸载旧的类定义,并加载新的类定义,从而实现热部署和动态更新。
3. 扩展类路径:
自定义类加载器可以扩展Java的类路径(classpath)。默认情况下,Java的类加载器只会在指定的类路径下查找类。但是,通过使用自定义类加载器,可以添加额外的搜索路径,如从数据库、网络位置或其他非传统文件系统位置加载类。
4. 代码加密和安全性:
自定义类加载器可以用于加载加密的类文件,并在加载过程中解密它们。这增加了代码的安全性,因为即使有人能够访问到加密的类文件,也无法直接读取或修改它们。此外,自定义类加载器还可以实现更细粒度的安全性控制,例如只允许加载来自特定源的类。
5. 自定义加载策略:
自定义类加载器允许开发者实现自己的类加载策略。例如,可以根据类的名称、包名或其他属性来决定是否加载该类,或者如何在不同的类加载器之间共享类。这种灵活性使得开发者能够更好地控制类的加载和卸载行为。
实现复杂的类加载逻辑:
在某些情况下,开发者可能需要实现复杂的类加载逻辑,如根据类的依赖关系动态加载类、根据类的版本信息选择加载哪个版本的类等。通过使用自定义类加载器,可以轻松地实现这些复杂的类加载逻辑。
6. 支持OSGi等模块化系统:
OSGi(Open Service Gateway initiative)是一个用于构建模块化Java应用程序的框架。它使用自定义类加载器来实现模块的隔离和动态加载。类似地,其他模块化系统也可能需要使用自定义类加载器来支持其特定的功能和需求。
类加载器架构以及三大类加载器说明
说自定义类加载器前,我们不得不说下 Java 的类加载器架构
这里简单说明下各个类加载
- 引导类加载器(BootstrapClassLoader):
这个类加载器是使用C/C++语言实现的,并且它是JVM(Java虚拟机)内部的一个组成部分。其主要功能是加载Java的核心库,这些库通常位于JAVA_HOME/jre/lib/rt.jar文件或指定的sun.boot.class.path路径下。通过这种方式,JVM能够确保在运行时正确地加载和访问Java的核心类库。 - 拓展类加载器(ExtensionClassLoader):
它是ClassLoader类的子类,负责从java.ext.dirs系统属性所指定的目录中加载类库,以及从JDK安装目录的jre/lib/ext子目录下加载类库。这些目录通常包含扩展的Java类库,包括由第三方提供的JAR文件。因此,如果用户将自定义的JAR文件放置在这些目录下,它们将自动由扩展类加载器在运行时加载到Java虚拟机中,从而无需在应用程序中显式引用或加载这些类库。通过 parent 属性指定父加载器,父加载器为引导类加载器。 - 应用程序类加载器(ApplicationClassLoader):
也称为系统类加载器(System ClassLoader),它同样是ClassLoader类的子类,并且其父类加载器通常是拓展类加载器(Extension ClassLoader)。系统类加载器主要负责加载环境变量classpath或系统属性java.class.path所指定路径下的类库。在Java应用程序中,系统类加载器是默认的类加载器,用于加载用户自定义的类和应用程序依赖的库。它也是用户自定义类加载器的默认父加载器,提供了类加载的基本框架和功能。通过调用ClassLoader类的静态方法getSystemClassLoader(),可以获取到系统类加载器的实例,从而可以进一步访问和定制类加载的行为。
双亲委派机制说明
引导类加载器先加载,若加载不到,由扩展类加载器加载,若还加载不到,才会由系统类加载器或自定义的类加载器进行加载。
具体实现自定义类加载器
这里在不破坏双亲委派机制的条件下实现自定义类加载器,具体代码如下
public class CustomClassLoader extends ClassLoader{
private String realPath;
public CustomClassLoader( String realPath) {
this.realPath = realPath;
}
/**
*
* @param name
* The <a href="#name">binary name</a> of the class
* loadClass 方法会调用 findClass ,重写 findClass 方法不会破坏双亲委派机制
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = getBytes(filePath);
assert bytes != null;
return defineClass(name,bytes,0,bytes.length);
}
private byte[] getBytes(String filePath) {
// 返回加载到的类的字节码
return null;
}
}