文章目录
- 1. 为什么要自定义类加载器
- 1.1 隔离加载类
- 1.2 修改类加载的方式
- 1.3 扩展加载源
- 1.4 防止源码泄漏
- 2. 自定义类加载器应用场景有哪些
- 3. 两种实现方式
自定义类加载器是Java中的一个高级特性,允许您在运行时动态加载类。通过自定义类加载器,您可以实现一些特定的功能,比如从非标准位置加载类、实现类的热部署等。
前面,我们介绍了类加载器的原理,以及类加载的源码。本篇文章,我们结合前面内容,自定义一个类加载器,以及使用自定义的类加载来完成类加载操作。另外还会介其为什么要自定义类加载器和其应用场景。
1. 为什么要自定义类加载器
1.1 隔离加载类
在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。再比如:Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序。 (类的仲裁–>类冲突)
1.2 修改类加载的方式
类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载
1.3 扩展加载源
比如从数据库、网络、甚至是电视机机顶盒进行加载
1.4 防止源码泄漏
Java代码容易被编译和篡改,可以进行编译加密。那么类加载也需要自定义,还原加密的字节码。
2. 自定义类加载器应用场景有哪些
实现类似进程内隔离,类加载器实际上用作不同的命名空间,以提供类似容器、模块化的效果。例如,两个模块依赖于某个类库的不同版本,如果分别被不同的容器加载,就可以互不干扰。这个方面的集大成者是Java EE和OSGI、JPMS等框架。
应用需要从不同的数据源获取类定义信息,例如网络数据源,而不是本地文件系统。或者是需要自己操纵字节码,动态修改或者生成类型。
注意:
在一般情况下,使用不同的类加载器去加载不同的功能模块,会提高应用程序的安全性。但是,如果涉及Java类型转换,则加载器反而容易产生不美好的事情。在做Java类型转换时,只有两个类型都是由同一个加载器所加载,才能进行类型转换,否则转换时会发生异常。
3. 两种实现方式
这两种方法本质上差不多,毕竟 loadClass() 也会调用 findClass(),但是从逻辑上讲我们最好不要直接修改loadClass() 的内部逻辑。建议的做法是只在 findClass() 里重写自定义类的加载方法,根据参数指定类的名字,返回对应的 Class 对象的引用。
public class CustomClassLoader extends ClassLoader {@Overridepublic Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] classData = loadClassData(name);return defineClass(name, classData, 0, classData.length);} catch (Exception e) {e.printStackTrace();return super.findClass(name);}}private byte[] loadClassData(String className) throws IOException {// 在这里实现加载类字节码的逻辑,可以从文件、网络等位置加载类的字节码// 这里只是一个示例,实际中需要根据需要实现具体的逻辑// 例如,可以从指定位置读取类的字节码文件InputStream inputStream = getClass().getClassLoader().getResourceAsStream(className.replace(".", "/") + ".class");ByteArrayOutputStream byteStream = new ByteArrayOutputStream();int nextValue = inputStream.read();while (nextValue != -1) {byteStream.write(nextValue);nextValue = inputStream.read();}return byteStream.toByteArray();}public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {CustomClassLoader customClassLoader = new CustomClassLoader();Class<?> customClass = customClassLoader.loadClass("com.example.CustomClass");// 实例化加载的类Object customObject = customClass.newInstance();System.out.println("Class loaded by: " + customClass.getClassLoader());}
}
如上面的代码所示: CustomClassLoader 继承自ClassLoader,重写了 findClass 方法和 loadClassData方法。findClass 方法用于查找类,loadClassData 方法用于加载类的字节码数据。在 main 方法中,演示了如何使用自定义类加载器加载类并实例化对象。