[英]How to dynamically create a Groovy Closure from a String in Java
I would like to know how to create a Closure
object at run-time from within a Java application, where the content of the Closure is not known ahead of time. 我想知道如何在Java应用程序中在运行时创建
Closure
对象,其中Closure的内容未提前知道。 I have found a solution but I doubt that it is optimal. 我找到了解决方案,但我怀疑它是最佳的。
Background: I have written some Groovy code that parses a Domain Specific Language. 背景:我编写了一些解析域特定语言的Groovy代码。 The parsing code is statically compiled and included in a Java application.
解析代码是静态编译的,并包含在Java应用程序中。 In the parser implementation I have classes acting as delegates for specific sections of the DSL.
在解析器实现中,我有类充当DSL的特定部分的委托。 These classes are invoked using the following pattern:
使用以下模式调用这些类:
class DslDelegate {
private Configuration configuration
def section(@DelegatesTo(SectionDelegate) Closure cl) {
cl.delegate = new SectionDelegate(configuration)
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
}
}
I wish to call such a method directly from Java code. 我希望直接从Java代码中调用这样的方法。 I am able to create a new
DslDelegate
object and then invoke the section()
method. 我能够创建一个新的
DslDelegate
对象,然后调用section()
方法。 However I need to create and pass an argument that is an instance of Closure
. 但是我需要创建并传递一个参数,该参数是
Closure
一个实例。 I want the content to be initialised from a String
object. 我希望从
String
对象初始化内容。
My Solution: The following Java code (utility) is working but I am asking for improvements. 我的解决方案:以下Java代码(实用程序)正在运行,但我要求改进。 Surely this can be done in a cleaner or more efficient manner?
当然,这可以更清洁或更有效的方式完成吗?
/**
* Build a Groovy Closure dynamically
*
* @param strings
* an array of strings for the text of the Closure
* @return a Groovy Closure comprising the specified text from {@code strings}
* @throws IOException
*/
public Closure<?> buildClosure(String... strings) throws IOException {
Closure<?> closure = null;
// Create a method returning a closure
StringBuilder sb = new StringBuilder("def closure() { { script -> ");
sb.append(String.join("\n", strings));
sb.append(" } }");
// Create an anonymous class for the method
GroovyClassLoader loader = new GroovyClassLoader();
Class<?> groovyClass = loader.parseClass(sb.toString());
try {
// Create an instance of the class
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
// Invoke the object's method and thus obtain the closure
closure = (Closure<?>) groovyObject.invokeMethod("closure", null);
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
} finally {
loader.close();
}
return closure;
}
You can use GroovyShell
to create a Closure
from strings: 您可以使用
GroovyShell
从字符串创建Closure
:
public Closure<?> buildClosure(String... strings) {
String scriptText = "{ script -> " + String.join("\n", strings) + " }";
return (Closure<?>) new GroovyShell().evaluate(scriptText);
}
Thanks to @hzpz I've soved the similar task, but I'd made it more beautiful and easy-to-use. 感谢@hzpz,我已经完成了类似的任务,但我让它变得更美观,更易于使用。 In my case the closure might accept any arguments, so I put arguments list to closure
s code. Let
在我的情况下,闭包可能接受任何参数,所以我把参数列表放在闭包
s code. Let
s code. Let
s say the closure dynamically created in the String and looks like this: s code. Let
在String中动态创建闭包,看起来像这样:
script1 = 'out,a,b,c-> out.println "a=${a}; b=${b}; c=${c}"; return a+b+c;'
Now, create new method in the String
class 现在,在
String
类中创建新方法
String.metaClass.toClosure = {
return (Closure) new GroovyShell().evaluate("{${delegate}}")
}
Now I can call a closure from String or file or from anything else. 现在我可以从String或文件或其他任何东西调用闭包。
println script1.toClosure()(out,1,2,3)
or 要么
println (new File('/folder/script1.groovy')).getText('UTF-8').toClosure()(out,1,2,3)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.