手写一个JVM自定义类加载器
创始人
2024-11-18 14:06:54
0

1. 自定义类加载器的意义

  • 隔离加载类:在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。再比如:Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序。 (类的仲裁–>类冲突)
  • 修改类加载的方式:类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载
  • 扩展加载源:比如从数据库、网络、甚至是电视机机顶盒进行加载。
  • 防止源码泄漏:Java代码容易被编译和篡改,可以进行编译加密。那么类加载也需要自定义,还原加密的字节码。

2. 手写一个自定义类加载器

import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException;  /**  * @author polaris  * @version 1.0  * ClassName UserClassLoader  * Package main.java.classloader  * Description  * @create 2024-07-21 20:06  */ public class CustomerClassLoader extends ClassLoader{     private String rootPath;     public CustomerClassLoader (String rootPath){         this.rootPath = rootPath;     }     @Override     protected Class findClass(String name) throws ClassNotFoundException {         String filePath = rootPath + "\\" + name.replace(".", "\\") + ".class";         //获取指定路径的class文件对应的二进制流数据         byte[] data = getBytesFromPath(filePath);         return defineClass(name,data,0,data.length);     }     public byte[] getBytesFromPath(String path){         FileInputStream fis = null;         ByteArrayOutputStream baos = null;         try {             fis = new FileInputStream(path);             baos = new ByteArrayOutputStream();             byte[] buffer = new byte[1024];             int len;             while((len=fis.read(buffer))!=-1){                 baos.write(buffer,0,len);             }             return baos.toByteArray();         } catch (IOException e) {             e.printStackTrace();         } finally {             try {                 assert fis != null;                 fis.close();             } catch (IOException e) {                 e.printStackTrace();             }             try {                 assert baos != null;                 baos.close();             } catch (IOException e) {                 e.printStackTrace();             }         }         return new byte[0];     }     public static void main (String[] args){         try {             String rootDir = "G:\\JavaLearning2023\\20_JVMJava虚拟机\\尚硅谷宋红康JVM精讲与GC调优\\JVMdachang\\chapter02_classload\\src";              //######################自定义类加载器1             CustomerClassLoader loader1 = new CustomerClassLoader(rootDir);             Class clazz1 = loader1.loadClass("com.atguigu.java3.User");             System.out.println("----------自定义类加载器1----------");             System.out.println("获取到的Class1实例:"+clazz1);             System.out.println("Class1的类加载器:"+clazz1.getClassLoader());             System.out.println("类加载器的父类加载器:"+clazz1.getClassLoader().getParent());             Class clazz11 = loader1.findClass("com.atguigu.java3.User");             System.out.println("获取到的Class11实例:"+clazz11);             System.out.println("Class11的类加载器:"+clazz11.getClassLoader());             System.out.println("类加载器的父类加载器:"+clazz11.getClassLoader().getParent());              //######################自定义类加载器2             CustomerClassLoader loader2 = new CustomerClassLoader(rootDir);             Class clazz2 = loader2.findClass("com.atguigu.java3.User");             System.out.println("----------自定义类加载器2----------");             System.out.println("获取到的Class2实例:"+clazz2);             System.out.println("Class2的类加载器:"+clazz2.getClassLoader());             System.out.println("类加载器的父类加载器:"+clazz2.getClassLoader().getParent());             System.out.println("clazz1==clazz2 :" + (clazz1 == clazz2));//clazz1与clazz2对应了不同的类模板结构。             System.out.println("clazz11==clazz2 :" + (clazz11 == clazz2));//clazz11与clazz2对应了不同的类模板结构。             Class clazz22 = loader2.loadClass("com.atguigu.java3.User");             System.out.println("获取到的Class22实例:"+clazz22);             System.out.println("Class22的类加载器:"+clazz22.getClassLoader());             System.out.println("类加载器的父类加载器:"+clazz22.getClassLoader().getParent());             System.out.println("clazz22==clazz2 :" + (clazz22 == clazz2));//clazz22与clazz2对应了相同的类模板结构。             //######################自定义类加载器3             CustomerClassLoader loader3 = new CustomerClassLoader(rootDir);             Class clazz3 = loader3.loadClass("com.atguigu.java3.User");             System.out.println("----------自定义类加载器3----------");             System.out.println("获取到的Class3实例:"+clazz3);             System.out.println("Class3的类加载器:"+clazz3.getClassLoader());             System.out.println("类加载器的父类加载器:"+clazz3.getClassLoader().getParent());             System.out.println("clazz1==clazz3 :" + (clazz1 == clazz3));//clazz1与clazz3对应了相同的类模板结构。             //######################系统类加载器             System.out.println("----------系统类加载器----------");             Class clazz4 = ClassLoader.getSystemClassLoader().loadClass("com.atguigu.java3.User");             System.out.println("获取到的Class4实例:"+clazz4);             System.out.println("Class4的类加载器:"+clazz4.getClassLoader());             System.out.println("类加载器的父类加载器:"+clazz4.getClassLoader().getParent());             System.out.println("clazz1==clazz4 :" + (clazz1 == clazz4));//clazz1与clazz4对应了相同的类模板结构。             //######################new对象的加载器             System.out.println("----------new对象的加载器----------");             com.atguigu.java3.User user = new com.atguigu.java3.User();             Class clazz5 = user.getClass();             System.out.println("获取到的Class5实例:"+clazz5);             System.out.println("Class5的类加载器:"+clazz5.getClassLoader());             System.out.println("类加载器的父类加载器:"+clazz5.getClassLoader().getParent());             System.out.println("clazz1==clazz5 :" + (clazz1 == clazz5));//clazz1与clazz5对应了相同的类模板结构。         } catch (ClassNotFoundException e) {             throw new RuntimeException(e);         }     } } 输出 ----------自定义类加载器1---------- 获取到的Class1实例:class com.atguigu.java3.User Class1的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d 获取到的Class11实例:class com.atguigu.java3.User Class11的类加载器:com.atguigu.java3.CustomerClassLoader@7ea987ac 类加载器的父类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 ----------自定义类加载器2---------- 获取到的Class2实例:class com.atguigu.java3.User Class2的类加载器:com.atguigu.java3.CustomerClassLoader@29453f44 类加载器的父类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 clazz1==clazz2 :false clazz11==clazz2 :false 获取到的Class22实例:class com.atguigu.java3.User Class22的类加载器:com.atguigu.java3.CustomerClassLoader@29453f44 类加载器的父类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 clazz22==clazz2 :true ----------自定义类加载器3---------- 获取到的Class3实例:class com.atguigu.java3.User Class3的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d clazz1==clazz3 :true ----------系统类加载器---------- 获取到的Class4实例:class com.atguigu.java3.User Class4的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d clazz1==clazz4 :true ----------new对象的加载器---------- 获取到的Class5实例:class com.atguigu.java3.User Class5的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d clazz1==clazz5 :true 

说明:

在自定义类加载器1中

用**loadClass()**方法加载,虽然是自定义类加载器,但是还是用的是系统类加载器AppClassLoader,其父类加载器是扩展类ExtClassLoader,这是因为基于双亲委派机制,会先向上层父类加载器请求加载,这里AppClassLoader正好管理了classpath下的所有类,就加载了User类,就没有loader1什么事了。

用**findClass()**方法加载,跳过了双亲委派机制,直接用当前类加载器加载,所以是CustomerClassLoader,其父类加载器是AppClassLoader。

而在自定义类加载器2中

用**findClass()**方法加载跳过了双亲委派机制,直接用当前类加载器加载,所以是CustomerClassLoader,其父类加载器是AppClassLoader。虽然都是自定义类加载器,但是是不同的类加载器实例,所以获取到Class实例clazz11和clazz2不同,clazz1和clazz就更不等了。

用**loadClass()**方法加载,会先检查是否已经被加载过了,发现已经用CustomerClassLoader加载过了,就会直接返回clazz2。

在自定义类加载器3中

还是用**loadClass()**方法加载,会先检查是否已经被加载过了,还没有加载过,根据双亲委派机制用AppClassLoader加载,但系统类加载器共用了clazz1的,已经加载过直接返回实例,所以clazz3和clazz1相同。

在系统类加载器中

用loadclass()方法加载,会先检查是否已经被加载过了,系统类加载器共用了clazz1的,已经加载过直接返回实例,所以clazz4和clazz1相同。

new的对象的加载器

由于new的时classpath下的类,所以默认用系统类加载器,系统类加载器共用了clazz1的,已经加载过直接返回实例,所以clazz5和clazz1相同。

注意:不同的类加载器实例的缓存(是否已经加载过)是不共通的,所以clazz4并不会等于clazz2,而系统类加载器和扩展类加载器会公用一个,加载过就不会再次加载,直接返回(所以说类很难卸载,系统类加载器会有很多引用)。

相关内容

热门资讯

专业讨论!德扑之星真破解套路(... 专业讨论!德扑之星真破解套路(辅助挂)软件透明挂(有挂了解)-哔哩哔哩;人气非常高,ai更新快且高清...
每日必看!智星德州菠萝外挂检测... 每日必看!智星德州菠萝外挂检测(辅助挂)软件透明挂(有挂教学)-哔哩哔哩1、玩家可以在智星德州菠萝外...
透视透明挂!轰趴十三水有后台(... 轰趴十三水有后台赢率提升策略‌;透视透明挂!轰趴十三水有后台(辅助挂)软件透明挂(有挂详情)-哔哩哔...
发现玩家!德扑ai助手软件(辅... 发现玩家!德扑ai助手软件(辅助挂)透视辅助(有挂教学)-哔哩哔哩;玩家在德扑ai助手软件中需先进行...
一分钟了解!x-poker辅助... 一分钟了解!x-poker辅助软件(辅助挂)辅助透视(有挂攻略)-哔哩哔哩1、每一步都需要思考,不同...
一分钟揭秘!德州最新辅助器(辅... 一分钟揭秘!德州最新辅助器(辅助挂)透视辅助(有挂攻略)-哔哩哔哩;德州最新辅助器最新版本免费下载安...
玩家攻略推荐!德州辅助(辅助挂... 玩家攻略推荐!德州辅助(辅助挂)辅助透视(有挂了解)-哔哩哔哩是由北京得德州辅助黑科技有限公司精心研...
揭秘真相!pokernow德州... 《揭秘真相!pokernow德州(辅助挂)辅助透视(有挂介绍)-哔哩哔哩》 pokernow德州软件...
五分钟了解!德州之星辅助器(辅... 五分钟了解!德州之星辅助器(辅助挂)辅助透视(有挂透明)-哔哩哔哩1、很好的工具软件,可以解锁游戏的...
推荐一款!pokermaste... 1、推荐一款!pokermaster有外挂(辅助挂)透视辅助(有挂教学)-哔哩哔哩;详细教程。2、p...