返回顶部
首页 > 资讯 > 后端开发 > Python >Java ClassLoader虚拟类实现代码热替换的示例代码
  • 261
分享到

Java ClassLoader虚拟类实现代码热替换的示例代码

2024-04-02 19:04:59 261人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录总结ClassLoader 虚拟类方法实现代码热替换实现改进思考总结 类加载器是负责加载类的对象。类ClassLoader是一个抽象类。给定类的全限定类名,类加载器应尝试查找或生

总结

  1. 类加载器是负责加载类的对象。类ClassLoader是一个抽象类。给定类的全限定类名,类加载器应尝试查找或生成构成该类定义的数据Class文件。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的类文件
  2. 每个Class对象都包含一个Class.getClassLoader()方法可以获取到定义它的ClassLoader
  3. 数组类的Class对象不是由类加载器创建的,而是根据Java运行时的要求自动创建的。getClassLoader()返回的数组类的类装入器与其元素类型的类装入器相同,如果元素类型是基础类型,则数组类没有类装入器。
  4. 除了加载类之外,类加载器还负责定位资源。资源是一些数据(例如 .class文件、配置数据或图像)。资源通常与应用程序或库一起打包,以便可以通过应用程序或库中的代码找到它们。
  5. ClassLoader类使用委托模型来搜索类和资源。ClassLoader的每个实例都有一个关联的父类加载器。当请求查找类或资源时,ClassLoader实例通常会在尝试查找类或资源本身之前,将对该类或资源的搜索委托给其父类装入器。
  6. Java内置类加载器
加载器名方法名作用
Bootstrap class loader虚拟机的内置类加载器,底层是用c++实现的,没有父加载器。主要加载系统环境的一些jar包和.class文件。C/C++语言编写,是虚拟机的一部分,无法在Java代码中直接获取它的引用。可以通过 System.getProperty(“sun.boot.class.path”)获取其加载路径下的文件
PlatfORM class loader (也称为ExtClassLoader)getPlatformClassLoader()平台类加载器,负责加载jdk中一些特殊的模块。主要加载java.ext.dirs下的.class文件
System class loader (也称为AppClassLoader)getSystemClassLoader()系统类加载器,负责加载用户类路径上所指定的类库。主要加载java.class.path下的.class文件,是面向用户编写类的类加载器,即自己写的类或者引入的第三方库通常由此加载器加载
  1. 通常Java虚拟机以依赖于平台的方式从本地文件系统加载类。但是,有些类可能不是源于文件;它们可能来自其他来源,如网络,也可能由应用程序构建。 方法defineClass(String name, byte[] b, int off, int len),将字节数组转换为类class的实例。这个新定义的类的实例可以使用Class.newInstance()方法创建
  2. 类加载器创建的对象的方法和构造函数可以引用其他类。为了确定引用的类,Java虚拟机调用最初创建该类的类加载器的loadClass方法

ClassLoader 虚拟类方法

方法名作用
protected ClassLoader(String name, ClassLoader parent)创建指定名称name的新类加载器,并使用指定的父类加载器parent进行委派
public String getName()返回此类加载器的名称,如果此类加载器未命名,则返回null
public Class loadClass(String name, boolean resolve)加载具有指定类名称的类,resolve为true表示解析类引用。loadClass方法会先调用getClassLoadingLock方法获取,再调用findLoadedClass方法检查类是否已加载,如果未加载,则往父加载器一直递归调用loadClass加载该类,如果父加载器也加载不了该类,才调用findClass方法获取Class对象,而findClass是虚拟方法由子类实现。其实现使用defineClass(String name, byte[] b, int off, int len)方法可以将class文件的字节数组转为Class对象,最后使用resolveClass方法进行类的链接。而由于获取该字节数组的方法是很多样的,所以类加载的方式也非常多样,如本地加载、网络加载、压缩包中加载、自己构建Class文件
protected Object getClassLoadingLock(String className)返回类加载操作的锁对象。 如果此ClassLoader对象注册为支持并行,则该方法返回与指定类名 className关联的专用对象。否则该方法将返回此ClassLoader对象,即同一时间一个ClassLoader只能加载一个类
protected final Class findLoadedClass(String name)如果Java虚拟机已将此加载程序记录为具有给定全限定类名称的类的初始加载程序,则返回具有给定全限定类名称的类。否则返回 null
protected Class findClass(String name)查找具有指定全限定类名的类。这个方法是空方法应该被子类重写,并且被调用在检查请求类的父类加载器之后,loadClass方法将调用这个方法
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)将字节数组转换为具有给定保护域ProtectionDomain的类Class的实例。 如果指定的name以“java.”开头,它只能由getPlatformClassLoader()获取到的平台类加载器或其祖先定义(define),否则将抛出SecurityException。如果name不是null,则它必须等于字节数组b指定的类的全限定类名称,否则将抛出NoClassDefFoundError
protected final Class<?> defineClass(String name, java.NIO.ByteBuffer b, ProtectionDomain protectionDomain)将字节缓冲区ByteBuffer转换为具有给定保护域ProtectionDomain的类Class的实例。其余和上面一样
protected final void resolveClass(Class c)链接指定的类。类加载器可能会使用此方法链接类。如果类c已经被链接,那么这个方法直接返回。 否则,将按照Java语言规范执行一章中的描述链接该类

实现代码热替换

通过上面我们可以知道类加载流程是

  1. loadClass方法会先调用getClassLoadingLock方法获取锁
  2. 再调用findLoadedClass方法检查类是否已加载,如果已经加载则直接获取到该Class类对象,再判断该Class类对象是否需要链接(resolve),如果要链接进入resolveClass,链接完后直接返回。链接就是执行类加载过程中的验证、准备、解析这些过程
  3. 如果未加载,则往父加载器一直递归调用loadClass加载该类
  4. 如果父加载器也加载不了该类,才调用findClass方法获取Class对象而findClass是空方法由子类重写
  5. 其实现中会使用defineClass(String name, byte[] b, int off, int len)方法,该可以将class文件的字节数组转为Class对象
  6. 如果需要链接,最后使用resolveClass方法进行类的链接

实现

  1. 如果我们要实现代码热替换,那么就要使用defineClass方法加载新的类,所以最简单的实现就是直接使用defineClass方法将新的Class文件字节数组转为Class对象,再使用反射创建新对象并执行新方法
  2. 但是defineClass是protected方法,所以我们只能继承ClassLoader虚拟类才能调用该方法
  3. 最好的规范就是继承ClassLoader虚拟类,并实现其loadClass方法和findClass方法,并在loadClass方法中调用findClass方法,在findClass方法中再调用defineClass方法
  4. 为了便于理解,直接抛弃规范,直接自己写一个方法直接调用defineClass方法实现代码热替换

项目结构,out是编译出的class文件目录,由于Test就在src目录下,没有包名,则其全限定类名为Test

public class Test extends ClassLoader {
    public static void main(String[] args) throws Exception {
        while(true) {
            try {
                Test test = new Test();
                // 编译后的class文件位置 ./表示代码根目录
                String classFile = "./out/production/Java_hot_replace/Test.class";
                FileInputStream fis = new FileInputStream(classFile);
                byte[] bytes = new byte[1024*10];
                int len = fis.read(bytes);
                //将字节数组转为Class类对象 Test为全限定类名
                Class clazz = test.defineClass("Test", bytes, 0 ,len);
                //使用反射根据新的Class对象创建新对象,并执行其printStr方法
                Object object = clazz.newInstance();
                Method m = object.getClass().getMethod("printStr", new Class[] {});
                m.invoke(object, new Object[] {});
                Thread.sleep(2000);
            } catch(Exception e) {
                e.printStackTrace();
                try {
                    Thread.sleep(2000);
                } catch(InterruptedException ex) {

                }
            }
        }
    }

    public void printStr() {
        System.out.println("A");
    }
}

启动后修改代码,然后点重新编译

可以看到代码被热替换了

改进思考

  • 正常实现流程应该为继承ClassLoader虚拟类,并重写其loadClass方法和findClass方法,并在loadClass方法中调用findClass方法,在findClass方法中再调用defineClass方法
  • 实现热替换应该是替换修改过的代码,则应当维护一个Map<String, Long> 存储从全限定类名到上次文件修改时间的映射,每次定时扫描Class文件目录或检测到保存快捷键Ctrl+s时触发扫描,文件的属性也有上次修改时间,拿我们存储的和文件的属性比较即可知道文件是否修改,即是否需要重新加载Class类
  • 热替换产生了大量类信息都存储在jdk1.7的永久代,jdk1.8的元空间,如果无用的类信息过多则会造成OOM,我们自定义类加载器和其产生的Class类对象,都可以通过置空(= null)使其不可达,然后调用System.GC()就可以卸载,即类似如下代码
public class Test extends ClassLoader {
   public static void main(String[] args) throws Exception {
     	MyClassLoader classLoader = new MyClassLoader();
 	 	Class classLoaded = classLoader.loadClass("MyClass");
  	 	classLoaded = null; 
  		classLoader = null; 
  	 	System.gc();  
  	}
}

到此这篇关于Java ClassLoader虚拟类实现代码热替换的示例代码的文章就介绍到这了,更多相关Java ClassLoader代码热替换内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java ClassLoader虚拟类实现代码热替换的示例代码

本文链接: https://lsjlt.com/news/152923.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java ClassLoader虚拟类实现代码热替换的示例代码
    目录总结ClassLoader 虚拟类方法实现代码热替换实现改进思考总结 类加载器是负责加载类的对象。类ClassLoader是一个抽象类。给定类的全限定类名,类加载器应尝试查找或生...
    99+
    2024-04-02
  • Webpack 实现 Node.js 代码热替换
    这两天为了这个问题, Gitter 上问, Twitter 上问, GitHub 上问, 两天没反应 原来写博客的 jlongster 不理我, 我也不知道 Webpack 作者的联系方式 最后在 Gitt...
    99+
    2022-06-04
    代码 Webpack Node
  • C语言实现字符串替换的示例代码
     替换,意思就是用另一个字符串str3来替换str1中所有的str2。替换过程和查找的过程可以合并在一起,在上面循环查找的过程中,每找到一个str2,就把它替换为str3,...
    99+
    2024-04-02
  • vue轻松实现虚拟滚动的示例代码
    目录前言 滚动原理 实现 源代码 参考前言 移动端网页的日常开发中,偶尔会包含一些渲染长列表的场景.比如某旅游网站需要完全展示出全国的城市列表,再有将所有通讯录的姓名按照A,B,C...
    99+
    2024-04-02
  • Java正则替换手机号代码实例
    在日常生活中,我们经常会遇到将一个手机号的4-7位字符串用正则表达式替换为为星号“*”。这是出于对安全性和保护客户隐私的考虑将程序设计成这样的。下面我们就来看看具体代码。package Test0914;public class Mobil...
    99+
    2023-05-31
    java 正则表达式 ava
  • java中replaceAll替换圆括号实例代码
    前言 在手写sql的时候,根据参数处理查询条件. select * from staff where 1 = 1 and staff_id in ($staffI...
    99+
    2024-04-02
  • Java代码实现循环队列的示例代码
    循环队列结构 队列特点 队列为一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受...
    99+
    2024-04-02
  • 基于Java实现进制转换工具类的示例代码
    目录背景原理十进制A转换为N进制RN进制R转换为十进制A应用延伸背景 最近有个发送短信的功能,需要在短信中带有详情链接,链接中带有对应信息且要有校验功能,然而短信是按字数收费的,所以...
    99+
    2023-02-19
    Java进制转换工具类 Java进制转换
  • java正则替换sql中的参数实例代码
    目录前言要求:分析:代码:测试:总结:前言 在处理sql参数的时候,替换圆括号里面只处理了一种情况。而没有从整体上进行处理!!! 这是一个思考问题上严重的偏向。 考虑问题时候,要先从...
    99+
    2024-04-02
  • C++模拟实现vector的示例代码
    目录1.前言2.vector介绍3.vector模拟实现3.1 迭代器接口3.2 vector元素操作3. 3 构造与析构1.前言 大家在学习C++的时候一定会学到STL(标准模板库...
    99+
    2024-04-02
  • SpringBoot 替换 if 的参数校验示例代码
    目录简单使用引入依赖requestBody参数校验requestParam/PathVariable参数校验统一异常处理进阶使用分组校验嵌套校验集合校验自定义校验编程式校验快速失败(...
    99+
    2022-12-14
    SpringBoot 参数校验 SpringBoot 替换 if  参数校验
  • 利用JavaScript实现创建虚拟键盘的示例代码
    目录前言项目基本结构JavaScript 虚拟键盘的显示虚拟键盘的按钮CSS的键盘按钮设计使用 JavaScript 激活虚拟键盘前言 在线演示地址 项目基本结构 目录结构如下: ...
    99+
    2024-04-02
  • Java实现Treap树的示例代码
    目录Treap树数据结构遍历查询增加删除完整代码Treap树 Treap树是平衡二叉搜索树的一种实现方式,但它不是完全平衡的。平衡二叉搜索树的实现方式还有AVL树、红黑树、替罪羊树、...
    99+
    2024-04-02
  • Go实现set类型的示例代码
    目录如何实现set构造一个Set如何实现set Go中是不提供Set类型的,Set是一个集合,其本质就是一个List,只是List里的元素不能重复。 Go提供了map类型,但是我们知...
    99+
    2023-01-31
    Go set类型 Go set
  • C语言实现字符串替换的示例代码怎么写
    本篇文章给大家分享的是有关C语言实现字符串替换的示例代码怎么写,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。 替换,意思就是用另一个字符串str3来替换str1中所有...
    99+
    2023-06-26
  • OpenCV实现视频绿幕背景替换功能的示例代码
    目录1、概述2、代码示例1、概述 案例:使用OpenCV实现视频绿幕背景替换 算法步骤: 1.初始化VideoCapture并使用其open方法加载视频 2.while循环加读取fr...
    99+
    2023-02-19
    OpenCV视频绿幕背景替换 OpenCV绿幕背景替换 OpenCV 背景替换
  • Java实现模拟机器人对话的示例代码
    目录前言一、Java多线程的介绍 二、创建线程并运行三、多线程间的交互前言 今天带大家来体验一下Java多线程,首先我们要明白什么是线程?什么是多线程? 进程是指一个内存中...
    99+
    2024-04-02
  • C++中priority_queue模拟实现的代码示例
    目录priority_queue概述 priority_queue定义 priority_queue特点 构造函数 修改相关函数pushpop容量相关函数sizeempty元素访问相...
    99+
    2024-04-02
  • Java实现滑动验证码的示例代码
    目录1.效果演示2.后端代码控制层工具类3.前端页面功能:java实现滑动验证码 项目是采用springboot,maven 开发工具:采用idea 1.效果演示 2.后端代码...
    99+
    2024-04-02
  • Java实现短信验证码的示例代码
    目录项目需求需求来由代码实现发送验证码方法注册方法忘记密码前端代码编码中遇到的问题如何改进短信验证码相信大家都不陌生吗,但是短信验证码怎么生成的你真的了解吗,本文揭示本人项目中对短信...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作