首页 > 其他分享 >Spring Loaded代码热更新实践和原理分析

Spring Loaded代码热更新实践和原理分析

时间:2023-07-05 12:11:06浏览次数:56  
标签:Spring 代码 say JVM Loaded 方法 加载

1、引言

开发者在编码效率和快速迭代中的痛点场景包括:

  1. 修改代码后,需要频繁重启应用,导致开发效率低下;

  2. 实时调试时,不能立即看到代码修改的结果;

  3. 大型项目中,重启的时间成本较高。

针对这些问题,本文将深入探讨如何利用Spring Loaded热更新技术提高开发效率,减少编译和重启时间。分析Spring Loaded的热更新原理,以及实际应用过程中所需的操作和注意事项。

2、框架简介

Spring Loaded is a JVM agent for reloading class file changes whilst a JVM is running. It transforms classes at loadtime to make them amenable to later reloading. Unlike 'hot code replace' which only allows simple changes once a JVM is running (e.g. changes to method bodies), Spring Loaded allows you to add/modify/delete methods/fields/constructors. The annotations on types/methods/fields/constructors can also be modified and it is possible to add/remove/change values in enum types.

Spring Loaded 是一个 JVM 代理,可以在 JVM 运行时重新加载类文件的更改。它会在加载时转换类,以便稍后重新加载。与“热代码替换”只允许在 JVM 运行时进行简单更改(例如更改方法体)不同,Spring Loaded 允许您添加/修改/删除方法/字段/构造函数。还可以修改类型/方法/字段/构造函数上的注解,并且可以添加/删除/更改枚举类型中的值。

3、如何使用

3.1 下载Agent插件

https://repo1.maven.org/maven2/org/springframework/springloaded/1.2.8.RELEASE/springloaded-1.2.8.RELEASE.jar

3.2 引入Agent插件

在jvm的启动命令中添加以下参数

-javaagent:/Users/you/runtime/springloaded-1.2.8.RELEASE.jar -noverify

3.3 修改并重新编译

修改代码后执行Build->Recompile命令,可以看到在class reloaded完成后,程序的运行逻辑发生了变化

4、原理分析

4.1 代码编译分析

先来看一段源代码,这是一个RpcService类,定义了target字段、targetStatic静态字段和say方法,现在我们编译它。

public class RpcService {
    private String target = "rpc";
    private static String targetStatic = "rpc static";
    public String say() {
        return "RpcService say hello SpringLoaded" + target;
    }
}

SpringLoaded对类编译后添加了一些跟踪记录字段,添加方法拦截判断。

public static ReloadableType r$type = TypeRegistry.getReloadableType(0, 1);

public transient ISMgr r$fields;

public static final SSMgr r$sfields;

public String hello() {
    if (r$type.changed(0) == 1) {
        return r$type.fetchLatest()).say(this);
    }
    String targetNew = TypeRegistry.instanceFieldInterceptionRequired(1, "target") ? (String)r$get(this, "target") : this.target;
    return "RpcService say hello SpringLoaded" + targetNew;
}

我们可以在代码运行时,使用getDeclaredField、getDeclaredMethod等函数在运行时获取类成员、方法信息,此时可以看到增强后的类多了如下字段和方法。

在编译后的代码中,我们可以看到RpcService类包含了一些新的字段和方法,这些都是Spring Loaded框架增加的。

•r$type是一个静态变量,其类型为ReloadableType。这个字段用于表示当前类的可重载类型,它包含了当前类的最新字节码和其他相关信息。

  • r\(get、r\)set方法是用于获取实例字段的值的方法,处理字段的拦截和替换。
  • ___clinit___方法是用于执行类的静态初始化块的方法。
  • init()方法是用于处理类的构造函数的方法。
  • 在say()方法中增加了一个代码片段用于判断类是否发生了变更,如果变更了,则调用最新的可重载类型中的say()方法获取结果。否则,继续执行原有的方法体。在方法体中,也增加了一个代码片段用于判断本地变量是否需要拦截,如果需要,则使用r$get()方法获取非静态变量traget的值,并用它替换原有的变量值。

4.2 运行过程分析

1、在应用程序启动时,Spring Loaded在目标类路径中查找所有的类,并在ClassPreProcessor中使用自定义类加载器加载这些类,重新定义后存入TypeRegistry,用于缓存、变更对比和依赖关系维护。

2、注册一个文件变化监听器FileChangeListener,当一个类文件被修改后,Spring Loaded会检测到这个变化,并重新加载该类文件。

3、当一个类被重新加载时,Spring Loaded会尝试对比类的签名和继承关系没有改变,如果新的类定义与之前的类定义兼容,那么Spring Loaded会更新应用程序中的对象引用,以指向新的类定义。

5、总结

Spring-loaded 使用 Java 的 Instrumentation API 在 JVM 启动时指定 Agent,使它能够在目标类加载之前进行拦截,并将目标类的字节码通过 ASM 库解析成抽象语法树(AST),然后对 AST 进行修改。修改的内容包括增加、删除、替换方法,修改方法体,添加字段等,最终替换目标类,改变其逻辑,实现对代码的热更新。

6、扩展内容

•Jrebel也可以实现类似热更新功能,并且它更高效、稳定。jrebel官网

•Spring-boot-devtools也可以提升开发速度,但是它的方案更像是热重启。Spring Boot Devtools Restarter 原理

•如何自己实现一个热更新功能呢?思路大同小异,实现各有千秋。如何自己实现一个热加载?如何定义自己的类加载器?

作者:京东零售 程啸

来源:京东云开发者社区

标签:Spring,代码,say,JVM,Loaded,方法,加载
From: https://www.cnblogs.com/jingdongkeji/p/17528195.html

相关文章

  • Springboot No bean named 'XXXXX' available 问题解决
    一、问题描述近日在工作中遇见了一个bug,后端程序频频报错Nobeannamed'XXXXX'available。对比同类程序文件,没有发现有任何特殊之处。在网上搜索方法基本上就是扫描包配置、注解问题、路径问题等,皆不能解决我的问题。排查问题是发现出现问题的类命名不符合驼峰规范,按照这个......
  • 关于spring-cloud-starter-alibaba-nacos-discovery找不到,没有版本(pom爆红,找不到)
    #一,我这边发现每次项目都有经常发生这样的问题问题:这种问题说实话很弱智,但还是有类似于这种事情发生啊原因:1.在POM依赖没有版本的时候,可能原因是父工程或者父依赖中没有指定版本,所以在子工程中找不到版本(本次错误)2.还有就是父工程有版本,或者子工程也有版本的时候,此时就是本地......
  • Spring配置文件中,bean标签下是各个子标签的作用解释
    bean标签的子标签propertyconstructor-argdescriptionlookup-methodmetaqualifierreplaced-method在Spring配置文件中,bean标签下是各个标签的作用解释:<property>:用于设置bean的属性值。它可以用于注入基本类型、引用类型或其他属性。通过指定属性名称和对应的值,可以......
  • 代码随想录算法训练营第二十四天| 491.递增子序列 46.全排列 47.全排列 II
     491.递增子序列 此题的难点:1,前提需要保留原有顺序2,保证递增3,保证去重注意:去重一定要有set的同时保证有顺序代码:1voidfindSubsequences_trackBack(vector<int>&nums,intstartIndex,vector<int>&path,vector<vector<int>>&result)2{3if(path.size(......
  • 在线JS代码混淆加密工具真的能实现JS代码加密吗?
    很多时候我们网站的JS代码都有被拷贝的风险,于是JS代码混淆加密这种能避免大部分复制的方法便出现了,可以对自己的JS加密,加密后达到安全的作用,不用担心自己辛辛苦苦写的JS给别人抄袭了。然而一段JS代码要是自己去编辑加密的话就有点费时费力了,目前加密工具也非常多,在线JS代码混淆......
  • 利用GPT快速生成自动化代码
    背景上个月整理了业务的接口,发现需要编写较多的接口自动化代码,除了一些写接口,大部分查询接口都是通过调用-断言的方式编写,存在较多冗余代码,思考能否快速编写,然后去问GPT回答1先问GPT有没有快速生成指定格式的代码的方法,它推荐了使用模版按照它推荐的方法,生成单个接口的自动......
  • springboot Minio功能实现
    1.导入Minio相关依赖<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.4.4</version><exclusions><exclusion><groupId>c......
  • SpringBoot源码初学者(一):SpringBoot功能扩展接口的使用与源码分析
     在长期的源码学习中,我一直没有找到一个讲解SpringBoot源码的文章,适合刚开始阅读源码的新手,包括我之前的写的一些文章,说实话SpringBoot的源码的确不适合新手,跳转多、结构复杂混乱,很容易迷乱其中。长时间的学习当中,也总结出这些文章的部分问题:没有说明用法,直接讲解源码,其实很......
  • 从零搭建SpringBoot3一,手动编写一套属于自己风格的代码生成器一键生成系统
    简介虽然java的代码生成工具有很多,可是很多时候不是自己喜欢的风格,改起来比较困难,所以我准备从零和大家一起搭建一套基于springboot3.0的框架,这次就先搞定一套代码生成功能,后续再不断的完善其它我们使用到的三方库:beelt模版引擎,用于生成代码。官网:http://ibeetl.com......
  • Java17新特性及代码示例:还在使用Java8? 这5个Java17新功能,你会喜欢的
    SpringBoot3.0最低支持JDK17,各开源软件正在全面拥抱JDK17.有升级计划的调查受访者中,37%的人计划在未来六个月内升级到2021年9月份发布的LTS版本JDK17。另有25%的人计划在未来6到12个月内升级到JDK17。这5个你喜欢的JDK17新功能,会让你升级JDK17吗?1.record类(记录类)传统的......