避免在不同的Groovy脚本之间共享Java元类 我的处境
我从Java调用多个Groovy脚本,它们都包含长寿命的Groovy对象 我希望我的Groovy脚本对Java类的Java元类进行一些更改(大约有100个实例)。但是,脚本应该能够进行不同的更改,其中一个脚本中的更改不应该反映在其他脚本中 问题是:Java类的元类在所有脚本中共享 这个问题类似于,但在本例中,我希望同时执行两个脚本,因此在脚本执行后无法重置 示例代码 SameTest.java避免在不同的Groovy脚本之间共享Java元类 我的处境,java,groovy,metaclass,groovyshell,Java,Groovy,Metaclass,Groovyshell,我从Java调用多个Groovy脚本,它们都包含长寿命的Groovy对象 我希望我的Groovy脚本对Java类的Java元类进行一些更改(大约有100个实例)。但是,脚本应该能够进行不同的更改,其中一个脚本中的更改不应该反映在其他脚本中 问题是:Java类的元类在所有脚本中共享 这个问题类似于,但在本例中,我希望同时执行两个脚本,因此在脚本执行后无法重置 示例代码 SameTest.java public interface SameTest { void print();
public interface SameTest {
void print();
void addMyMeta(String name);
void addJavaMeta(String name);
void callMyMeta(String name);
void callJavaMeta(String name);
}
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
public class SameSame {
public SameTest launchNew() {
try {
GroovyScriptEngine scriptEngine = new GroovyScriptEngine(new String[]{""});
Binding binding = new Binding();
binding.setVariable("objJava", this);
SameTest script = (SameTest) scriptEngine.run("test.groovy", binding);
return script;
} catch (Exception | AssertionError e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
SameSame obj = new SameSame();
SameTest a = obj.launchNew();
SameTest b = obj.launchNew();
a.addMyMeta("a");
a.callMyMeta("a");
try {
b.callMyMeta("a");
throw new AssertionError("Should never happen");
} catch (Exception ex) {
System.out.println("Exception caught: " + ex);
}
a.addJavaMeta("q");
b.callJavaMeta("q");
a.print();
b.print();
}
}
SameSame.java
public interface SameTest {
void print();
void addMyMeta(String name);
void addJavaMeta(String name);
void callMyMeta(String name);
void callJavaMeta(String name);
}
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
public class SameSame {
public SameTest launchNew() {
try {
GroovyScriptEngine scriptEngine = new GroovyScriptEngine(new String[]{""});
Binding binding = new Binding();
binding.setVariable("objJava", this);
SameTest script = (SameTest) scriptEngine.run("test.groovy", binding);
return script;
} catch (Exception | AssertionError e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
SameSame obj = new SameSame();
SameTest a = obj.launchNew();
SameTest b = obj.launchNew();
a.addMyMeta("a");
a.callMyMeta("a");
try {
b.callMyMeta("a");
throw new AssertionError("Should never happen");
} catch (Exception ex) {
System.out.println("Exception caught: " + ex);
}
a.addJavaMeta("q");
b.callJavaMeta("q");
a.print();
b.print();
}
}
test.groovy
ExpandoMetaClass.enableGlobally()
class Test implements SameTest {
SameSame objJava
void print() {
println 'My meta class is ' + Test.metaClass
println 'Java meta is ' + SameSame.metaClass
}
void addMyMeta(String name) {
println "Adding to Groovy: $this $name"
this.metaClass."$name" << {
"$name works!"
}
}
void addJavaMeta(String name) {
println "Adding to Java: $this $name"
objJava.metaClass."$name" << {
"$name works!"
}
}
void callMyMeta(String name) {
println "Calling Groovy: $this $name..."
"$name"()
println "Calling Groovy: $this $name...DONE!"
}
void callJavaMeta(String name) {
println "Calling Java: $this $name..."
objJava."$name"()
println "Calling Java: $this $name...DONE!"
}
}
new Test(objJava: objJava)
期望结果
显示Java元信息的两行应该是不同的
这应该会崩溃:
a.addJavaMeta("q");
b.callJavaMeta("q");
问题
在不同的GroovyScriptEngine
实例中是否可以使用不同的MetaClassRegistry
或者是否有其他方法可以实现上面所示的预期结果?您正在寻找的功能是我为Groovy 3计划的功能。但是,由于我将不再能够在Groovy上全职工作,而且因为没有其他人敢对MOP进行大的更改,所以目前这不是一个选项 那么,在不同的GroovyScriptEngine实例中是否可以使用不同的MetaClassRegistry 不可以,因为您不能使用不同的MetaClassRegistry。实现有些抽象,但MetaClassRegistryImpl的使用是硬编码的,只允许一个全局版本 或者,是否有其他方法可以实现上述所示的预期结果 这取决于你的要求
- 如果您可以让脚本不共享Java类(使用不同的类加载器加载它们),那么您一开始就不会有共享元类的问题。如果你想要更多,这个想法可能是最好的
- 您可以提供自己的元类创建句柄(请参见
中的MetaClassRegistry
)。然后您当然必须捕获一个调用,如setMetaClassCreationHandle
。您可以将ExpandoMetaClass.enableglobal()
与自定义调用程序一起使用(设置ExpandoMetaClass
),当然也可以直接扩展该类。然后,您可能需要一种方法来识别您来自一个脚本或另一个脚本(在较大的invokemethod签名中有一种称为someClass.metaClass.invokeMethod=…
或origin
,但信息并不总是可靠的。get/setProperty也是如此)。至于如何可靠有效地传输这些信息。。。好。。这是我没有答案的事。您必须试验ExpandoMetaClass提供的内容是否适合您。也许你可以使用ThreadLocal来存储信息。。。不过,您必须编写一个转换,它将重写所有方法和属性调用,并且很可能导致性能灾难caller
groovyscript引擎
,并将新的空Classloader
作为第二个参数提供,没有任何更改。如果需要类加载器魔法,我想它会比那个微弱的尝试复杂一点。是的。。。null
classloader表示系统类加载器。您需要>>差异,您需要自定义类加载器。可能已经有了这方面的库。正如另一个问题中提到的,我自己做了-