Моя ситуация
Я вызываю несколько сценариев Groovy из Java, они оба содержат долговечные объекты Groovy.
Я хотел бы, чтобы мои сценарии Groovy внесли некоторые изменения в мета-класс Java для Java-класса (у которого около 100 экземпляров). Тем не менее, сценарии должны иметь возможность делать разные изменения, а изменения в одном из сценариев не должны отражаться в других сценариях.
Проблема: метаклассом класса Java является общий доступ ко всем скриптам.
Этот вопрос похож на Как мне отменить изменения метакласса после выполнения GroovyShell?, но в этом случае я хочу, чтобы два сценария выполнялись одновременно, поэтому это невозможно до reset после выполнения script.
Пример кода
SameTest.java
public interface SameTest {
void print();
void addMyMeta(String name);
void addJavaMeta(String name);
void callMyMeta(String name);
void callJavaMeta(String name);
}
SameSame.java
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)
Выход
Adding to Groovy: [email protected] a
Calling Groovy: [email protected] a...
Calling Groovy: [email protected] a...DONE!
Calling Groovy: [email protected] a...
Exception caught: groovy.lang.MissingMethodException: No signature of method: Test.a() is applicable for argument types: () values: []
Possible solutions: any(), any(groovy.lang.Closure), is(java.lang.Object), wait(), wait(long), each(groovy.lang.Closure)
Adding to Java: [email protected] q
Calling Java: [email protected] q...
Calling Java: [email protected] q...DONE!
My meta class is [email protected][class Test]
Java meta is [email protected][class SameSame]
My meta class is [email protected][class Test]
Java meta is [email protected][class SameSame]
Желаемый результат
Две строки, показывающие информацию о метатете Java, должны быть разными.
Это должно произойти сбой:
a.addJavaMeta("q");
b.callJavaMeta("q");
Вопрос
Возможно ли каким-то образом использовать разные MetaClassRegistry
в разных экземплярах GroovyScriptEngine
?
Или есть ли другой способ сделать желаемый результат, как показано выше?