Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
避免在不同的Groovy脚本之间共享Java元类 我的处境_Java_Groovy_Metaclass_Groovyshell - Fatal编程技术网

避免在不同的Groovy脚本之间共享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();

我从Java调用多个Groovy脚本,它们都包含长寿命的Groovy对象

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

问题是:Java类的元类在所有脚本中共享

这个问题类似于,但在本例中,我希望同时执行两个脚本,因此在脚本执行后无法重置

示例代码 SameTest.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();

    }

}
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
    与自定义调用程序一起使用(设置
    someClass.metaClass.invokeMethod=…
    ),当然也可以直接扩展该类。然后,您可能需要一种方法来识别您来自一个脚本或另一个脚本(在较大的invokemethod签名中有一种称为
    origin
    caller
    ,但信息并不总是可靠的。get/setProperty也是如此)。至于如何可靠有效地传输这些信息。。。好。。这是我没有答案的事。您必须试验ExpandoMetaClass提供的内容是否适合您。也许你可以使用ThreadLocal来存储信息。。。不过,您必须编写一个转换,它将重写所有方法和属性调用,并且很可能导致性能灾难

Classloader magic required?@StephenC我尝试创建
groovyscript引擎
,并将新的空
Classloader
作为第二个参数提供,没有任何更改。如果需要类加载器魔法,我想它会比那个微弱的尝试复杂一点。是的。。。
null
classloader表示系统类加载器。您需要>>差异,您需要自定义类加载器。可能已经有了这方面的库。正如另一个问题中提到的,我自己做了-