Groovy热更新Java实践
之前在写Groovy动态添加方法和属性及Spock单测文章的时候,我还没意识到metaclass
的神奇之处,直到有一天我突然想要不经过构建过程直接更新功能,也就是传说中的热更新。
之前学过arthas的时候写过arthas命令redefine实现Java热更新的文章,之前看笨马在MTSC大会演示的功能差不多,不过是都是通过命令行手动触发的。如果通过服务调用命令,实在不是最优之选。
然后我就想到了Groovy的metaclass,就想到了通过groovy.lang.GroovyShell
执行上传的Groovy脚本,然后就可以达到一定程度的动态更新的需求。
说干就干,先写个正常版本的验证。
功能验证
我先创建一个类,然后定义一个对象方法,简单输出一个数字。然后在main
方法中创建两个对象,分别调用各自的test()
方法,这中间通过metaClass重新实现test()方法,输出FunTester
。
package com.funtest.groovytest
import com.funtester.frame.SourceCode
class HotUpdate extends SourceCode {
public static void main(String[] args) {
def update = new HotUpdate()
update.test()
HotUpdate.metaClass.test = {output("FunTester")}
def update2 = new HotUpdate()
update2.test()
}
public void test() {
output(123)
}
}
控制台输出:
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main
###### # # # # ####### ###### ##### ####### ###### #####
# # # ## # # # # # # # # #
# # # # # # # # # # # # #
#### # # # # # # #### ##### # #### #####
# # # # # # # # # # # # #
# # # # ## # # # # # # # #
# ##### # # # ###### ##### # ###### # #
INFO-> main 123
INFO-> main FunTester
Process finished with exit code 0
脚本实现
经过功能验证,路已经通了。现在就开始编写脚本,脚本内容就是把metaClass重新实现test()方法的功能脚本化:
import com.funtest.groovytest.HotUpdate
import com.funtester.frame.Output
HotUpdate.metaClass.test = {Output.output(\"FunTester\")}
接下来,我通过groovy.lang.GroovyShell
模拟服务器收到请求脚本(String类型参数)之后的功能。最后完整的代码就是:
import com.funtester.frame.SourceCode
import com.funtester.frame.execute.ExecuteGroovy
class HotUpdate extends SourceCode {
public static void main(String[] args) {
def update = new HotUpdate()
update.test()
ExecuteGroovy.executeScript("import com.funtest.groovytest.HotUpdate\n" +
"import com.funtester.frame.Output \n" +
"\n" +
"HotUpdate.metaClass.test = {Output.output(\"FunTester\")}")
def update2 = new HotUpdate()
update2.test()
}
public void test() {
output(123)
}
}
控制台输出:
INFO-> main 当前用户:oker,工作目录:/Users/oker/IdeaProjects/funtester/,系统编码格式:UTF-8,系统Mac OS X版本:10.16
INFO-> main
###### # # # # ####### ###### ##### ####### ###### #####
# # # ## # # # # # # # # #
# # # # # # # # # # # # #
#### # # # # # # #### ##### # #### #####
# # # # # # # # # # # # #
# # # # ## # # # # # # # #
# ##### # # # ###### ##### # ###### # #
INFO-> main 123
INFO-> main FunTester
Process finished with exit code 0
完美实现,中间在拷贝脚本的时候遇到一些问题,就是Intellij会自动把一些字符当做转义字符来处理,导致执行的脚本和实际脚本有了差异导致失败,这里建议大家尽量避免使用这种直接粘贴复制字符串的方式,转而使用上传脚本文件或者使用ngrinder的方案,将脚本存放在可访问的Git仓库中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南