当前位置: 首页 > news >正文

手写一个JVM自定义类加载器

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;}@Overrideprotected 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";//######################自定义类加载器1CustomerClassLoader 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());//######################自定义类加载器2CustomerClassLoader 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对应了相同的类模板结构。//######################自定义类加载器3CustomerClassLoader 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<? extends com.atguigu.java3.User> 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,而系统类加载器和扩展类加载器会公用一个,加载过就不会再次加载,直接返回(所以说类很难卸载,系统类加载器会有很多引用)。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • spring —— 事务管理器
  • Python - 开源库 ReportLab 库合并 CVS 和图像生成 PDF 文档
  • [网络编程】网络编程的基础使用
  • 【Drools】(二)基于业务需求动态生成 DRL 规则文件:事实与动作定义详解
  • Apache ShardingSphere Proxy5.5.0实现MySQL分库分表与读写分离
  • Halcon学习之边缘扩展
  • Java代理模式详解
  • React 的 KeepAlive 实战指南:深度解析组件缓存机制
  • 【网络爬虫技术】(1·绪论)
  • 深度学习高效性网络
  • 2024钉钉杯B题医疗门诊患者及用药数据案例分析
  • SolidWorks设计库的应用
  • 基于Golang+Vue3快速搭建的博客系统
  • 顺序表和单链表的代码实现
  • Ubuntu22.04安装Go语言的几种方式
  • .pyc 想到的一些问题
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • CSS 三角实现
  • docker容器内的网络抓包
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • golang中接口赋值与方法集
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • SwizzleMethod 黑魔法
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 闭包--闭包之tab栏切换(四)
  • 程序员该如何有效的找工作?
  • 从0到1:PostCSS 插件开发最佳实践
  • 分享一份非常强势的Android面试题
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 扑朔迷离的属性和特性【彻底弄清】
  • 如何进阶一名有竞争力的程序员?
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 为视图添加丝滑的水波纹
  • 学习HTTP相关知识笔记
  • 鱼骨图 - 如何绘制?
  • 翻译 | The Principles of OOD 面向对象设计原则
  • #### golang中【堆】的使用及底层 ####
  • (03)光刻——半导体电路的绘制
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (windows2012共享文件夹和防火墙设置
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (附源码)计算机毕业设计高校学生选课系统
  • (九)One-Wire总线-DS18B20
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (五)c52学习之旅-静态数码管
  • **CI中自动类加载的用法总结
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET CLR Hosting 简介
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .net/c# memcached 获取所有缓存键(keys)
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .pyc文件是什么?