如何在Java中运行Groovy脚本并保留状态以供后续使用?
在Java中运行Groovy脚本并保留运行状态
刚好处理过类似的场景,给你拆解成两部分详细说清楚,代码示例都给你写好了,直接能用:
1. 先搞定依赖
首先得确保项目里引入Groovy的核心库,如果用Maven的话,在pom.xml里加这段依赖(版本用最新稳定版就行):
<dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>3.0.17</version> <type>pom</type> </dependency>
2. 在Java中运行Groovy脚本
常用的有两种方式,选哪种看你的需求:
方式一:用Groovy原生的GroovyShell(灵活度更高)
这是Groovy官方推荐的方式,专门用来执行Groovy脚本,上手很简单:
import groovy.lang.Binding; import groovy.lang.GroovyShell; public class GroovyRunner { public static void main(String[] args) { // 从HTML里提取的Groovy脚本字符串 String groovyScript = """ def count = 0 def increment() { count++ return count } println("首次执行,count初始值: " + count) """; // 创建Binding对象,用来存脚本的变量和状态 Binding binding = new Binding(); // 绑定上下文到GroovyShell GroovyShell shell = new GroovyShell(binding); // 执行脚本 shell.evaluate(groovyScript); } }
方式二:用Java标准的ScriptEngine(兼容多脚本语言)
如果你的项目还需要支持其他脚本语言(比如JavaScript),用JSR-223标准的ScriptEngine更统一:
import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class GroovyScriptEngineRunner { public static void main(String[] args) throws ScriptException { String groovyScript = """ def count = 0 def increment() { count++ return count } println("首次执行,count初始值: " + count) """; // 获取Groovy脚本引擎 ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("groovy"); // 执行脚本 engine.eval(groovyScript); } }
3. 保留脚本运行状态,供后续调用
核心逻辑就是复用同一个上下文对象:GroovyShell用Binding,ScriptEngine用ScriptContext——脚本里的变量、方法都会存在这些上下文里,后续调用直接用同一个上下文就行,状态不会丢失。
用GroovyShell保留状态的完整示例
import groovy.lang.Binding; import groovy.lang.GroovyShell; import groovy.lang.Script; public class GroovyStatefulRunner { public static void main(String[] args) { String groovyScript = """ def count = 0 def increment() { count++ return count } def getCount() { return count } """; // 重点:全程复用这个Binding对象,它是状态的载体 Binding binding = new Binding(); GroovyShell shell = new GroovyShell(binding); // 先解析脚本成Script实例,避免重复编译(提升性能) Script scriptInstance = shell.parse(groovyScript); // 第一次执行,初始化状态 scriptInstance.run(); // 后续调用脚本里的increment方法,状态会保留 // 方式1:从Binding里拿方法引用调用 Runnable incrementFunc = (Runnable) binding.getVariable("increment"); incrementFunc.run(); // count变成1 incrementFunc.run(); // count变成2 // 获取当前count的值 Integer currentCount = (Integer) binding.getVariable("count"); System.out.println("当前count值:" + currentCount); // 输出2 // 方式2:直接通过Script实例调用方法 Object result = scriptInstance.invokeMethod("increment", null); System.out.println("调用increment后的值:" + result); // 输出3 } }
这里要注意:
- 一定要复用同一个
Binding,所有脚本的变量、方法都存在这里 - 如果脚本需要频繁执行,先
parse成Script实例,避免每次执行都重新编译脚本,性能会好很多
用ScriptEngine保留状态的完整示例
import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class GroovyEngineStatefulRunner { public static void main(String[] args) throws ScriptException, NoSuchMethodException { String groovyScript = """ def count = 0 def increment() { count++ return count } """; ScriptEngineManager manager = new ScriptEngineManager(); // 重点:复用同一个ScriptEngine实例,它内部维护了脚本的状态 ScriptEngine engine = manager.getEngineByName("groovy"); // 第一次执行脚本,初始化状态 engine.eval(groovyScript); // 要调用脚本里的方法,需要把engine转成Invocable接口 Invocable invocable = (Invocable) engine; // 后续调用方法,状态会保留 Object result1 = invocable.invokeFunction("increment"); System.out.println("第一次调用increment:" + result1); // 输出1 Object result2 = invocable.invokeFunction("increment"); System.out.println("第二次调用increment:" + result2); // 输出2 // 直接获取脚本里的count变量 Integer currentCount = (Integer) engine.get("count"); System.out.println("当前count值:" + currentCount); // 输出2 } }
这里的关键点:
- 复用同一个
ScriptEngine实例,它的内部上下文会保留脚本的所有状态 - 通过
Invocable接口可以直接调用脚本里定义的方法 - 用
engine.get("变量名")就能拿到脚本里的变量值
一些注意事项
- 线程安全:
GroovyShell和ScriptEngine都不是线程安全的,如果是多线程场景,建议每个线程用独立的实例,或者用ThreadLocal来管理上下文 - 性能优化:频繁执行的脚本一定要先解析成
Script实例(GroovyShell方式),避免重复编译 - 状态隔离:如果同时运行多个不同的脚本,要给每个脚本单独创建
Binding或ScriptContext,避免状态互相干扰
内容的提问来源于stack exchange,提问作者Roberto Ponte
相关产品推荐
相关产品推荐

