Java 调用 Groovy 脚本的简单案例
前言
Groovy
和Java
都是使用 JVM 虚拟机进行解释执行的。工作中会遇到一些场景,需要对特殊的业务进行解耦。那段业务可能会经常变动,如果直接在Java代码里写业务的话就会涉及频繁的发包服务重启这类情况,那么如果我门把那段业务变成脚本的形式,单独做一个界面把它做成类似可以在线配置更改的情况,那么业务变动后直接在线改代码,而且立刻生效,这样是不是就特别方便。例如定时任务里的xxx-job
也有这样的功能,我在以往的项目中也遇到过有人使用这种方式来解决这类问题,当年年少只是感叹他的神奇并没有尝试探索,这次抽空了解了下发现其实也不是很难,记录一下。
(一) 依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- groovy-all 这个是核心 版本号根据具体情况使用 --><dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy-all</artifactId><version>2.4.16</version></dependency>
(二)Groovy脚本
Groovy 脚本最好还是在IDEA里提前写好,然后粘过去,IDEA有代码提示和语法提示写起来很方便,少踩坑
def execute(paramModel) {def dateTime = paramModel.get('dateTime')def msg = paramModel.get('msg')def user = paramModel.get('user')println("now dateTime is:" + dateTime)println("message is:" + msg)println("user is:" + paramModel.get('user'))def a = 5def b = 3def c = '我是一个字符串用于进行拼接: 'def result = a + b * 3 + dateTime.getYear()def str = c.concat(msg).concat(",").concat(user.getName())println("result: " + result)println("str: " + str)return user.toString()
}
execute(paramModel)
(三) 单元测试代码
入参假设是一个
HashMap
的这类对象,可以存放多个入参。这个 script,可以是从数据库里读取出来的脚本,也就是我们线上修改后的业务。执行具体业务的时候,服务代码里只要从库里读取这个脚本,只要不涉及参数的新增和参数名(key)的修改,其它代码都不需要进行变动就能正常执行了。
/*** 脚本执行测试** @author lv* @date 2024/03/14*/
@Slf4j
@SpringBootTest
public class GroovyScriptTest {@Resourceprivate BeanConfig.User user;/*** Java 调用groovy脚本的 示范案例,下面是某次的执行结果* now dateTime is: 2024-03-14T16:01:27.966835100* message is: 你好世界* user is: BeanConfig.User(name=lzb, birthday=2000-01-15T23:45, height=180)* result=> 2038* str=> 我是一个字符串用于进行拼接: 你好世界,lzb* 2038*/@Testvoid simple() {// 这个 script 可以是从数据库中读取出来,得是下面的格式final String script = "def execute(paramModel) {\n" +" def dateTime = paramModel.get('dateTime')\n" +" def msg = paramModel.get('msg')\n" +" def user = paramModel.get('user')\n" +" println(\"now dateTime is: \" + dateTime)\n" +" println(\"message is: \" + msg)\n" +" println(\"user is: \" + paramModel.get('user'))\n" +"\n" +" def a = 5\n" +" def b = 3\n" +" def c = '我是一个字符串用于进行拼接: '\n" +" def result = a + b * 3 + dateTime.getYear()\n" +" def str = c.concat(msg).concat(\",\").concat(user.getName())\n" +"\n" +" println(\"result=> \" + result)\n" +" println(\"str=> \" + str)\n" +" return result\n" +"}\n" +"execute(paramModel)\n";// 构造函数的使用参数Map<String, Object> param = new HashMap<>();param.put("dateTime", LocalDateTime.now());param.put("msg", "你好世界");param.put("user", user);// 绑定参数Binding bind = new Binding();bind.setProperty("paramModel", param);// 执行groovy脚本获取结果GroovyShell groovyShell = new GroovyShell(bind);Object result = groovyShell.evaluate(script);System.out.println(result);}
}
被Spring管理的 User 配置类
@Configuration
public class BeanConfig {@Datapublic static class User implements Serializable {private String name;private LocalDateTime birthday;private int height;}@Beanpublic User user() {User user = new User();user.setName("lzb");user.setHeight(180);user.setBirthday(LocalDateTime.of(2000, 1, 15, 23, 45));return user;}
}
参考资料
Chart GPT