Spring Loaded代码热更新实践和原理分析 | 京东云技术团队

京东云开发者
• 阅读 337

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完成后,程序的运行逻辑发生了变化

Spring Loaded代码热更新实践和原理分析 | 京东云技术团队

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等函数在运行时获取类成员、方法信息,此时可以看到增强后的类多了如下字段和方法。

Spring Loaded代码热更新实践和原理分析 | 京东云技术团队

Spring Loaded代码热更新实践和原理分析 | 京东云技术团队

在编译后的代码中,我们可以看到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 原理

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

作者:京东零售 程啸

来源:京东云开发者社区

点赞
收藏
评论区
推荐文章
全靠这份Android知识点PDF大全,月薪30K
第一阶段:Android基础知识回顾:回顾Android开发编程,深入理解Android系统原理和层次结构,深入分析Handler源码和原理;回顾Java,C/C,Kotlin、dart在Android开发中必用的语言,熟悉一下几种语言混淆后的特性;回顾AndroidIPC和JNI的底层原理和热更新技术回顾Native开发要点,使用C结
佛系码 佛系码
3年前
Webpack 热更新以及原理
什么是热更新模块热替换(hotmodulereplacement或HMR)是webpack提供的最有用的功能之一。它允许在运行时更新所有类型的模块,而无需完全刷新一般的刷新我们分两种:一种是页面刷新,不保留页面状态,就是简单粗暴,直接window.location.reload()。另一种是基于WDS(Webpackde
Wesley13 Wesley13
3年前
unity热更(一)
Agenda•  什么是热更新•  为何要热更新•  如何在iOS 上对Unity 应用进行热更新•  支持UnityiOS 热更新的各种Lua 插件的对比什么是热更新• 广义定义• 无需关闭服务器,不停机状态下修复漏洞,更新资源等,重点是更新逻辑代码。• 狭义定义(iOS热更新)• 无需将代码重新打包提交至Ap
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
Java 调式、热部署、JVM 背后的支持者 Java Agent
1.探针的技术介绍各个JavaIDE的调试功能,例如eclipse、IntelliJ;热部署功能,例如JRebel、XRebel、springloaded;各种线上诊断工具,例如Btrace、Greys,还有阿里的Arthas;
Wesley13 Wesley13
3年前
MySQL数据库InnoDB存储引擎Log漫游(1)
作者:宋利兵来源:MySQL代码研究(mysqlcode)0、导读本文介绍了InnoDB引擎如何利用UndoLog和RedoLog来保证事务的原子性、持久性原理,以及InnoDB引擎实现UndoLog和RedoLog的基本思路。00–UndoLogUndoLog是为了实现事务的原子性,
Stella981 Stella981
3年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Stella981 Stella981
3年前
Spring Boot(十三):实现热部署
一、前言在实际开发过程中,每次修改代码就得将项目重启,重新部署,对于一些大型应用来说,重启时间需要花费大量的时间成本。对于一个后端开发者来说,重启过程确实很难受。在java开发领域,热部署一直是一个难以解决的问题,目前的java虚拟机只能实现方法体的热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作。!(htt
Flutter热更新技术探索 | 京东云技术团队
APP发布到市场后,难免会遇到严重的BUG阻碍用户使用,因此有在不发布新版本APP的情况下使用热更新技术立即修复BUG需求。原生APP(例如:Android&IOS)的热更新需求已经比较成熟,但Flutter技术栈目前还缺少类似的技术方案,因此Flutter研发团队,也需要类似的热更新技术。