避免跨不同Groovy脚本共享Java元类
我的情况
我从Java调用了多个Groovy脚本,它们都包含长久的Groovy对象. 我希望我的Groovy脚本对Java类(大约有100个实例)的Java元类进行一些更改.但是,脚本应该能够进行不同的更改,其中一个脚本中的更改不应该反映在其他脚本中. 问题: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(); } } 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的使用是硬编码的,并且只允许一个全局版本. 或者有什么其他方式可以产生如上所示的期望结果吗? 这取决于你的要求. >如果可以让脚本不共享Java类(使用不同的类加载器加载它们),那么开始使用共享元类没有问题(对于那些).如果你想要更多的想法bayou.io可能是最好的.>您可以提供自己的元类创建句柄(请参阅MetaClassRegistry中的setMetaClassCreationHandle).那么你当然会收到一个像ExpandoMetaClass.enableGlobally()这样的调用.您可以使用ExpandoMetaClass与自定义调用者(set someClass.metaClass.invokeMethod = …),或者当然可以直接扩展类.然后,您将需要一种方法来识别您是从一个脚本或另一个脚本(在较大的invokemethod签名中有一个称为origin或caller的东西,但是信息并不总是可靠的,get / setProperty的同样的东西).至于如何可靠,高效地传输这些信息…好的,这是我没有答案的东西.您必须尝试如果ExpandoMetaClass提供的是否足够好.也许你可以使用ThreadLocal来存储信息…虽然然后你必须编写一个转换,这将重写所有的方法和属性调用,最可能导致性能灾难. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |