我只是从Java中发现Groovy调用,并且遇到这种情况的问题:
我有一个普通文件:“ test.groovy
”
a = 1.0
def mul2( x ) { 2.0 * x }
我想从这样的Java代码中使用它
GroovyShell gs = new GroovyShell();
gs.parse( new File( ".../test.groovy" ) ).run();
System.out.printf( "a = %s%n", gs.evaluate("a") ); // ok
System.out.printf( "mul2(a) = %s%n", gs.evaluate( "mul2(a)" ) ); // error
错误是:
groovy.lang.MissingMethodException: No signature of method: Script1.mul2() is applicable for argument types: (BigDecimal) values: [1.0]
要使用valuate()方法访问groovy脚本中定义的函数,我该怎么做?
我需要使用“评估”方法,因为我最终希望评估类似Math.sin( a * mul2(Math.Pi) )
之类的东西。
现在我有4个解决方案(第四是我要搜索的):
...类(在Java中,不是Groovy)...
public static abstract class ScriptClass extends Script
{
double mul2( double x )
{
return x * 2;
}
}
...代码...
CompilerConfiguration config = new CompilerConfiguration();
config.setScriptBaseClass(ScriptClass.class.getName());
GroovyShell gs = new GroovyShell(config);
System.out.printf( "result = %s%n", gs.evaluate("mul2(5.05)") );
那行得通,但是代码是用Java编写的,不是我想要的,但是我在这里注意到了,因为需要这样做的人
常规文件:
double mul2( x ) { x * 2 }
a=mul2(3.33)
使用它的Java代码
GroovyClassLoader gcl = new GroovyClassLoader();
Class<?> r = gcl.parseClass( resourceToFile("/testx.groovy") );
CompilerConfiguration config = new CompilerConfiguration();
config.setScriptBaseClass(r.getName());
GroovyShell gs = new GroovyShell(gcl, config);
System.out.printf( "mul2(5.05) = %s%n", gs.evaluate("mul2(5.05)") );
// WARNING : call super.run() in evaluate expression to have access to variables defined in script
System.out.printf( "result = %s%n", gs.evaluate("super.run(); mul2(a) / 123.0") );
这正是我想要的:-)
答案 0 :(得分:2)
有两件事值得解释,以了解此处发生的情况。您提供的脚本中有两个不同的作用域。
变量a
被存储在GroovyShell
绑定对象中,这就是为什么它在每个gs.evaluate()
调用中都可用的原因。看一下这个例子:
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
final class ExecuteGroovyScriptExample {
public static void main(String[] args) {
final String script = "a = 1.0 \n" +
"def mul2(x) { 2.0 * x }\n";
final Binding binding = new Binding();
final GroovyShell gs = new GroovyShell(binding);
final Script sc = gs.parse(script);
sc.run();
System.out.printf("binding.getVariable(\"a\") == %s\n", binding.getVariable("a"));
}
}
运行此示例将产生以下输出:
binding.getVariable("a") == 1.0
第二件事是,每个gs.evaluate()
调用都会生成一个新的groovy.lang.Script
类,该类具有完全不同的上下文。这就是为什么调用:
gs.evaluate("mul2(a)")
抛出这样的东西:
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Script2.mul2() is applicable for argument types: (BigDecimal) values: [1.0]
因为从gs.evaluate("mul2(a)")
调用生成的脚本类不包含mul2(x)
方法。通过此调用生成的类如下所示:
class Script2 extends groovy.lang.Script {
void run() {
mul2(a)
}
}
但是,从gs.parse(script)
返回的脚本类包含mul2(x)
方法,因此您可以调用它,但不能作为gs.evaluate()
调用而是由Script.invokeMethod(name, args)
调用。像这样:
import groovy.lang.GroovyShell;
import groovy.lang.Script;
final class ExecuteGroovyScriptExample {
public static void main(String[] args) {
final String script = "a = 1.0 \n" +
"def mul2(x) { 2.0 * x }\n";
final GroovyShell gs = new GroovyShell();
final Script sc = gs.parse(script);
sc.run();
System.out.printf("mul2(a) = %s%n", sc.invokeMethod("mul2", gs.evaluate("a")));
}
}
此示例产生以下输出:
mul2(a) = 2.00
看看如何调用mul2(x)
方法。首先,我们将gs.parse(script)
返回的脚本存储在sc
变量中,它允许我们通过以下调用来调用此脚本中定义的方法:
sc.invokeMethod("mul2", gs.evaluate("a"));
在此示例中,我们仅通过a
来获取gs.evaluate("a")
变量的值,但是您也可以在第一个示例中使用binding
对象。请记住,如果a
变量的定义如下:
def a = 1.0
或
@groovy.transform.Field
def a = 1.0
它不再存储在binding
对象中,在第一种情况下,它定义了脚本的局部变量a
,在第二种情况下,它定义了脚本类字段a
。
或者,如果要执行以下调用:
gs.evaluate("mul2(a)")
甚至
gs.evaluate("Math.sin( a * mul2(Math.PI))")
您将必须修改输入Groovy脚本文件并用与mul2(x)
变量相同作用域的闭包替换函数a
定义,以便将其存储在binding
对象中:
a = 1.0
mul2 = { x -> 2.0 * x }
答案 1 :(得分:0)
让您拥有/my/groovy/classes/Test.groovy
:
static mul2( x ) { 2.0 * x }
def mul3( x ) { 3.0 * x }
然后,您可以使用类加载器将其加载为类,并在表达式中使用该类:
GroovyShell gs = new GroovyShell();
gs.getClassLoader().addClasspath("/my/groovy/classes");
System.out.println( gs.evaluate( "import static Test.*; mul2(5)" ) );
System.out.println( gs.evaluate( "new Test().mul3(6)" ) );