本文目录:
1、面试题
2、为什么使用自定义类加载器
3、应用场景
4、实现方式
5、代码demo
先上一道面试题
1、手写一个类加载器Demo (百度)
为什么要自定义类加载器?
- 隔离加载类
在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。再比如:Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序。(类的仲裁-->类冲突)
- 修改类加载的方式
类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载
- 扩展加载源
比如从数据库、网络、甚至是电视机机顶盒进行加载
- 防止源码泄漏
Java代码容易被编译和篡改,可以进行编译加密。那么类加载也需要自定义,还原加密的字节码。
应用场景
常见的应用场景
实现类似进程内隔离,类加载器实际上用作不同的命名空间,以提供类似容器、模块化的效果。例如,两个模块依赖于某个类库的不同版本,如果分别被不同的容器加载,就可以互不干扰。这个方面的集大成者是Java EE和OSGI、JPMS等框架。
应用需要从不同的数据源获取类定义信息,例如网络数据源,而不是本地文件系统。或者是需要自己操纵字节码,动态修改或者生成类型。
注意:
在一般情况下,使用不同的类加载器去加载不同的功能模块,会提高应用程序的安全性。但是,如果涉及Java类型转换,则加载器反而容易产生不美好的事情。在做Java类型转换时,只有两个类型都是由同一个加载器所加载,才能进行类型转换,否则转换时会发生异常。
实现方式
用户通过定制自己的类加载器,这样可以重新定义类的加载规则,以便实现一些自定义的处理逻辑。
实现方式
- Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类。
- 在自定义 ClassLoader 的子类时候,我们常见的会有两种做法:
- 方式一:重写loadClass()方法
- 方式二:重写findClass()方法 -->推荐
对比
这两种方法本质上差不多,毕竟loadClass()也会调用findClass(),但是从逻辑上讲我们最好不要直接修改loadClass()的内部逻辑。建议的做法是只在findClass()里重写自定义类的加载方法,根据参数指定类的名字,返回对应的Class对象的引用。
自定义类加载器的案例demo
public class MyTest16 extends ClassLoader {
private String classLoaderName;
private String fileExtension = ".class";
private String path;
public MyTest16(String classLoaderName) {
super(); // 将系统类加载器当做该类加载器的父类加载器
this.classLoaderName = classLoaderName;
}
public void setPath(String path){
this.path = path;
}
public MyTest16(ClassLoader parent, String classLoaderName) {
super(parent); //显示指定该类的加载器的父类加载器
this.classLoaderName = classLoaderName;
}
private byte[] loadClassData(String className) {
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
String classNameResult = className.replace(".", "\\");
System.out.println(classNameResult);
try {
//注意win和linux
this.classLoaderName = this.classLoaderName.replace(".", "\\");
//指定磁盘全路径
String fileUrl = classNameResult + this.fileExtension;
is = new FileInputStream(this.path + new File(fileUrl));
baos = new ByteArrayOutputStream();
int ch ;
while (-1 != (ch = is.read())) {
baos.write(ch);
}
// 字节数组输出流转换成字节数组
data = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return data;
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println("findClass invoked:" + className);
System.out.println("class loader name" + this.classLoaderName);
byte[] data = this.loadClassData(className);
//返回Class对象
return this.defineClass(className, data, 0 , data.length);
}
@Override
public String toString() {
return "[" + this.classLoaderName + "]";
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
// 创建自定义类加载器 名字“loader1” 父类加载器是系统类加载器
MyTest16 loader1 = new MyTest16("loader1");
//此路径为classPath,故 findClass方法不会被调用执行!如果换个路径,不是classPath就会去执行了!
loader1.setPath("D:\\eclipse_pj\\dianshang\\jvmTest\\out\\production\\jvmTest\\");
Class<?> clazz = loader1.loadClass("com.jvm.t1.MyTest15");
System.out.println("class:"+ clazz.hashCode());
Object object = clazz.newInstance();
System.out.println(object);
// loader1 指向新的实例
loader1 = new MyTest16("loader1");
//此路径为classPath,故 findClass方法不会被调用执行!如果换个路径,不是classPath就会去执行了!
loader1.setPath("D:\\eclipse_pj\\dianshang\\jvmTest\\out\\production\\jvmTest\\");
//指向新的
clazz = loader1.loadClass("com.jvm.t1.MyTest15");
System.out.println("class:"+ clazz.hashCode());
// 指向新的
object = clazz.newInstance();
System.out.println(object);
}
}
调用
public class Test17 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyTest16 loader1 = new MyTest16("loader1");
loader1.setPath("D:\\test\\jvm\\");
Class<?> clazz = loader1.loadClass("com.jvm.t1.test");
System.out.println("class"+ clazz.hashCode());
Object o = clazz.newInstance();
}
}
<script src="https://readmore.openwrite.cn/js/readmore.js" type="text/javascript"></script> <script> const btw = new BTWPlugin(); btw.init({ id: 'container', blogId: '17634-1639029113546-864', name: '大数据面试宝典', qrcode: 'https://gitee.com/wang_ao_qi/images/raw/master/mac_image/202112091351101.jpg', keyword: 'bigdata', }); </script>