Java 调用 Groovy 脚本的简单案例
前言
Groovy
和Java
都是使用 JVM 虚拟机进行解释执行的。工作中会遇到一些场景,需要对特殊的业务进行解耦。那段业务可能会经常变动,如果直接在Java代码里写业务的话就会涉及频繁的发包服务重启这类情况,那么如果我门把那段业务变成脚本的形式,单独做一个界面把它做成类似可以在线配置更改的情况,那么业务变动后直接在线改代码,而且立刻生效,这样是不是就特别方便。例如定时任务里的xxx-job
也有这样的功能,我在以往的项目中也遇到过有人使用这种方式来解决这类问题,当年年少只是感叹他的神奇并没有尝试探索,这次抽空了解了下发现其实也不是很难,记录一下。
(一) 依赖
xml
<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有代码提示和语法提示写起来很方便,少踩坑
groovy
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 = 5
def b = 3
def 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)的修改,其它代码都不需要进行变动就能正常执行了。
java
/**
* 脚本执行测试
*
* @author lv
* @date 2024/03/14
*/
@Slf4j
@SpringBootTest
public class GroovyScriptTest {
@Resource
private 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
*/
@Test
void 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 配置类
java
@Configuration
public class BeanConfig {
@Data
public static class User implements Serializable {
private String name;
private LocalDateTime birthday;
private int height;
}
@Bean
public 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