15

私の状況

私は Java から複数の Groovy スクリプトを呼び出します。どちらも有効期間の長い Groovy オブジェクトを含んでいます。

Groovy スクリプトで Java クラス (約 100 のインスタンスを持つ) の Java メタクラスに変更を加えたいと考えています。ただし、スクリプトはさまざまな変更を行うことができる必要があり、1 つのスクリプトの変更が他のスクリプトに反映されないようにする必要があります。

問題: Java クラスのメタクラスがすべてのスクリプトで共有されています。

この質問は、GroovyShell を実行した後にメタ クラスの変更を元に戻すにはどうすればよいですか? に似ています。ただし、この場合、2 つのスクリプトを同時に実行する必要があるため、スクリプトの実行後にリセットすることはできません。

サンプルコード

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 メタに関する情報を示す 2 行は異なるはずです。

これはクラッシュするはずです:

a.addJavaMeta("q");
b.callJavaMeta("q");

質問

MetaClassRegistry異なるGroovyScriptEngineインスタンスで異なる を使用することは何とか可能ですか?

または、上記のような望ましい結果を実現する他の方法はありますか?

4

1 に答える 1