写点什么

Groovy 热更新 Java 实践

作者:FunTester
  • 2021 年 12 月 01 日
  • 本文字数:2011 字

    阅读完需:约 7 分钟

之前在写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.16INFO-> main   ###### #     #  #    # ####### ######  #####  ####### ######  #####    #      #     #  ##   #    #    #       #   #     #    #       #    #   #      #     #  # #  #    #    #       #         #    #       #    #   ####   #     #  # #  #    #    ####    #####     #    ####    #####    #      #     #  #  # #    #    #            #    #    #       #   #     #      #     #  #   ##    #    #       #    #    #    #       #    #    #       #####   #    #    #    ######  #####     #    ######  #     # 
INFO-> main 123INFO-> main FunTester
Process finished with exit code 0
复制代码

脚本实现

经过功能验证,路已经通了。现在就开始编写脚本,脚本内容就是把 metaClass 重新实现 test()方法的功能脚本化:


import com.funtest.groovytest.HotUpdateimport com.funtester.frame.OutputHotUpdate.metaClass.test = {Output.output(\"FunTester\")}
复制代码


接下来,我通过groovy.lang.GroovyShell模拟服务器收到请求脚本(String 类型参数)之后的功能。最后完整的代码就是:



import com.funtester.frame.SourceCodeimport 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.16INFO-> main   ###### #     #  #    # ####### ######  #####  ####### ######  #####    #      #     #  ##   #    #    #       #   #     #    #       #    #   #      #     #  # #  #    #    #       #         #    #       #    #   ####   #     #  # #  #    #    ####    #####     #    ####    #####    #      #     #  #  # #    #    #            #    #    #       #   #     #      #     #  #   ##    #    #       #    #    #    #       #    #    #       #####   #    #    #    ######  #####     #    ######  #     # 
INFO-> main 123INFO-> main FunTester
Process finished with exit code 0
复制代码


完美实现,中间在拷贝脚本的时候遇到一些问题,就是 Intellij 会自动把一些字符当做转义字符来处理,导致执行的脚本和实际脚本有了差异导致失败,这里建议大家尽量避免使用这种直接粘贴复制字符串的方式,转而使用上传脚本文件或者使用 ngrinder 的方案,将脚本存放在可访问的 Git 仓库中。

欢迎关注 FunTester,Have Fun ~ Tester !

发布于: 2 小时前阅读数: 6
用户头像

FunTester

关注

公众号:FunTester,650+原创,欢迎关注 2020.10.20 加入

Have Fun,Tester! 公众号FunTester,坚持原创文章的测试人。 FunTester测试框架作者,DCS_FunTester分布式性能测试框架作者。

评论

发布
暂无评论
Groovy热更新Java实践