gpt4 book ai didi

java - 避免在不同的 Groovy 脚本之间共享 Java 元类

转载 作者:太空狗 更新时间:2023-10-29 22:58:08 27 4
gpt4 key购买 nike

我的情况

我从 Java 调用多个 Groovy 脚本,它们都包含长期存在的 Groovy 对象。

我希望我的 Groovy 脚本对 Java 类(大约有 100 个实例)的 Java 元类进行一些更改。但是,脚本应该能够进行不同的更改,其中一个脚本的更改不应反射(reflect)在其他脚本中。

问题:Java 类的元类在所有脚本之间共享。

这个问题类似于How do I undo meta class changes after executing 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();

}

}

测试.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 的使用是硬编码的,并且只允许一个全局版本。

或者是否有任何其他方法可以实现如上所示的预期结果?

这取决于您的要求。

  • 如果您可以让脚本不共享 Java 类(使用不同的类加载器加载它们),那么您开始使用共享元类就没有问题(对于那些)。如果你想要更多的想法bayou.io有可能是最好的。
  • 您可以提供自己的元类创建句柄(请参阅 MetaClassRegistry 中的 setMetaClassCreationHandle)。那么您当然必须捕获像 ExpandoMetaClass.enableGlobally() 这样的调用。您可以将 ExpandoMetaClass 与自定义调用程序一起使用(设置 someClass.metaClass.invokeMethod = ...),当然也可以直接扩展该类。然后你会以某种方式需要一种方法来识别你来自一个脚本或另一个脚本(在更大的 invokemethod 签名中有一个叫做 origincaller 的东西,但是信息并不总是可靠的。get/setProperty 也一样)。至于如何可靠且高效地传输该信息……好吧……这是我无法回答的问题。您必须试验 ExpandoMetaClass 提供的内容是否对您足够好。或许您可以使用 ThreadLocal 来存储信息……尽管那样您将不得不编写一个转换,这将重写所有方法和属性调用,并且很可能会导致性能灾难。

关于java - 避免在不同的 Groovy 脚本之间共享 Java 元类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30313834/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com