[英]Avoiding sharing Java meta classes across different Groovy scripts
我從Java調用多個Groovy腳本,它們都包含長期存在的Groovy對象。
我希望我的Groovy腳本能夠對Java類(具有大約100個實例)的Java元類進行一些更改。 但是,腳本應該能夠進行不同的更改,其中一個腳本中的更改不應該反映在其他腳本中。
問題:Java類的元類在所有腳本中共享。
此問題類似於在執行GroovyShell后如何撤消元類更改? 但在這種情況下,我希望同時執行兩個腳本,因此在腳本執行后無法重置。
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: Test@7ee955a8 a
Calling Groovy: Test@7ee955a8 a...
Calling Groovy: Test@7ee955a8 a...DONE!
Calling Groovy: Test@4a22f9e2 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: Test@7ee955a8 q
Calling Java: Test@4a22f9e2 q...
Calling Java: Test@4a22f9e2 q...DONE!
My meta class is groovy.lang.ExpandoMetaClass@2145b572[class Test]
Java meta is groovy.lang.ExpandoMetaClass@39529185[class SameSame]
My meta class is groovy.lang.ExpandoMetaClass@72f926e6[class Test]
Java meta is groovy.lang.ExpandoMetaClass@39529185[class SameSame]
顯示有關Java元信息的兩行應該是不同的。
這應該崩潰:
a.addJavaMeta("q");
b.callJavaMeta("q");
有可能以某種方式在不同的GroovyScriptEngine
實例中使用不同的MetaClassRegistry
嗎?
或者是否有其他方法可以實現上述所需的結果?
你正在尋找的功能是我為Groovy 3計划過的功能。但是由於我不再能夠在Groovy上全職工作,而且由於沒有其他人敢於對MOP進行大的改動,所以現在暫時沒有選擇。
那么可以在不同的GroovyScriptEngine實例中使用不同的MetaClassRegistry嗎?
不,因為你不能使用不同的MetaClassRegistry。 實現有點抽象,但MetaClassRegistryImpl的使用是硬編碼的,只允許一個全局版本。
或者是否有其他方法可以實現上述所需的結果?
這取決於您的要求。
MetaClassRegistry
setMetaClassCreationHandle
)。 然后你必須捕獲像ExpandoMetaClass.enableGlobally()
這樣的調用。 您可以將ExpandoMetaClass
與自定義調用程序一起使用(設置someClass.metaClass.invokeMethod = ...
),當然也可以直接擴展該類。 然后你會以某種方式需要一種方法來識別你來自一個腳本或另一個腳本(在更大的invokemethod簽名中有一個叫做origin
或caller
東西,但是信息並不總是可靠的。對於get / setProperty來說也是如此)。 至於如何可靠和有效地傳輸這些信息......那就是我無法回答的問題。 您必須嘗試ExpandoMetaClass提供的內容是否足夠好。 也許您可以使用ThreadLocal來存儲信息......但是您必須編寫一個轉換,它將重寫所有方法和屬性調用,並且最有可能導致性能災難。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.