JVM进阶之自定义类加载器
Posted ProChick
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM进阶之自定义类加载器相关的知识,希望对你有一定的参考价值。
1.作用
-
隔离加载类
在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。
-
修改类加载的方式
类的加载模型并非强制的,应该根据实际情况在某个时间点按需进行动态加载。
-
扩展加载源
可以从数据库、网络、甚至是电视机机顶盒进行加载。
-
防止源码泄露
可以对Java源码进行编译时的加密,还原时的解密。
2.场景
- 当实现类似进程内隔离效果时,类加载器可用作不同的命名空间,以提供类似容器、模块化的效果。
- 当应用需要从不同的数据源获取类定义信息时,比如:网络数据源、操纵字节码来动态修改或者生成类。
3.注意
- 在一般情况下,使用不同的类加载器去加载不同的功能模块,会提高应用程序的安全性。
- 但如果涉及Java类型之间的相互转换,那么只有两个类型都是由同一个类加载器所加载的,才能进行类型转换,否则转换时会发生异常。
4.实现
- 常见的有两种做法:重写loadClass方法、重写findClass方法。
- 这两种方法本质上差不多,毕竟在loadClass方法中也会调用findClass方法。但是从逻辑上讲,我们最好不要直接修改loadClass方法里面的内部逻辑, 因为这个方法是实现双亲委派模型逻辑的地方,擅自修改这个方法会导致模型被破坏,容易造成问题,所以建议使用第二种做法。
代码示例
public class MyClassLoader extends ClassLoader {
private String byteCodePath;
public MyClassLoader(String byteCodePath) {
this.byteCodePath = byteCodePath;
}
public MyClassLoader(ClassLoader parent, String byteCodePath) {
super(parent);
this.byteCodePath = byteCodePath;
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
BufferedInputStream bis = null;
ByteArrayOutputStream baos = null;
try {
// 获取字节码文件所在路径
String fileName = byteCodePath + className + ".class";
// 获取文件输入流
bis = new BufferedInputStream(new FileInputStream(fileName));
// 获取字节输出流
baos = new ByteArrayOutputStream();
// 将字节码文件写入到输出流当中
int len;
byte[] data = new byte[1024];
while ((len = bis.read(data)) != -1) {
baos.write(data, 0, len);
}
// 将字节输出流转化为字节数组
byte[] byteCodes = baos.toByteArray();
// 调用defineClass方法完成类实例的创建
Class clazz = defineClass(null, byteCodes, 0, byteCodes.length);
return clazz;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (baos != null) baos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null) bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
public class MyClassLoaderTest {
public static void main(String[] args) {
MyClassLoader loader = new MyClassLoader("字节码文件所在根路径");
try {
Class clazz = loader.loadClass("Demo");
System.out.println("加载此类的类加载器为:" +
clazz.getClassLoader().getClass().getName());
System.out.println("加载此类的类加载器的父加载器为:" +
clazz.getClassLoader().getParent().getClass().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
以上是关于JVM进阶之自定义类加载器的主要内容,如果未能解决你的问题,请参考以下文章