2

これを行うために groovy の invokeMethod を使用することを望んでいましたが、Java から Groovy を呼び出すと、invokeMethod は呼び出されませんが、それ以外の場合は完全に機能することがわかりました。

Groovy クラスを Java クラス (編集できない) に送信するケースがあります。Groovy クラスには注釈が付けられ、Java クラスは注釈をスキャンし、注釈付きのメソッドをそのイベントのリスナーとして保存します。

イベントが発行されたら、イベント オブジェクトから情報を取得し、それを使用してデータを取得し、そのデータをスクリプトのイベント ハンドラーに挿入します (そのメソッド内の注釈付き変数を介して)。

私が制御できることは、スクリプトをインスタンス化し、それらの基本クラスを設定し、それらを他のシステムに渡して登録することです。スクリプトは他の人によって書かれます - 私はスクリプトのデザインを制御できますが、私の目標はシンプルさです。

おそらくアダプター クラスを作成できますが、現在のように注釈を使用する代わりに、これらすべてのメソッドを手動で登録する必要があるため、非常に困難で壊れやすいようです。リッスンするさまざまなイベントがたくさんあります。

私が考えていないグルーヴィーなトリックがあるかどうか疑問に思っています。私はグルーヴィーなメタプログラミングにはまだかなり慣れていません。おそらく、アダプター クラスを自動的に作成する方法や、スクリプトをコンパイルするときに、メソッドを実際のメソッドを呼び出す前にコードに転送する転送メソッドに置き換える方法があります。

要求されたソース コード:

ソースコード - 見てみましょう、このプロセスはいくつかのクラスに分散しています...

これは、ScriptBase を使用して Groovy Class Loader をセットアップする方法です

cconfig.setScriptBaseClass("tv.kress.bill.minecraft.ezplugin.ScriptBase");
GroovyClassLoader gcl = new GroovyClassLoader(getClass().getClassLoader(), cconfig);

次に、それを Groovy Scripting Engine に渡します (ここでは一部省略しています)。

gse = new GroovyScriptEngine(cpString, gcl);

次に、スクリプトをインスタンス化します

scriptClass = gse.loadScriptByName(file.getAbsolutePath());
instance = (GroovyObject) scriptClass.newInstance();

次に、「定型化された」Java ライブラリが注釈をスキャンする必要がある Java クラスを識別するために使用するマーカー インターフェイスである「リスナー」である場合、注釈付きのメソッドを登録できるように、それをそのクラスに渡します (行「インスタンス」は「スクリプト」になりましたが、同じオブジェクトです:

 if (script instanceof Listener)
     pm.registerEvents((Listener) script, this);

スクリプト自体の興味深い部分は次のようになります。

@EventHandler
public void userEvent(UserInteractEvent event) {

追加したいのは、userEvent 内に、次のような注釈付きローカル変数を追加する機能です。

    @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?

userEvent が呼び出される直前にインターセプトできるようにします。UserInteractionEvent からユーザー名を取得し、それをスクリプト、変数、およびメソッド名と組み合わせて、"MyScript:UserEvent:Bill:persistedPerUserData" のような一意の署名を取得し、それを使用して、persistedPerUserData に配置できる int を取得します。

後でメソッドが返された後、persistedPerUserData から値を取得し、それを "MyScript:UserEvent:Bill:persistedPerUserData" に戻します (現在はハッシュですが、最終的にはデータベースにする予定です)。

このように、スクリプトはさまざまなユーザーを扱っているという事実を考慮する必要はありません。変数のセットが 1 つあれば、すべての永続性が機能します。

これが機能するイベントは他にもありますが、それらはすべて同じイベントを拡張し、そのルートイベントには「ユーザー」フィールドがあると思います。

編集:試してはいけないもう1つのこととして、次のようにProxyMetaClass/インターセプターを使用しようとしました:

// Attempt (and fail) to intercept calls to an instance of clazz
class Slicer {
    public static Object slice(Class clazz) {
        Object instance;
        def proxy = ProxyMetaClass.getInstance(clazz);
        proxy.interceptor = new MyInterceptor();
        proxy.use {
            instance = clazz.newInstance();
        }
        return instance;
    }       
}

同じ結果で、groovy クラスからのすべての呼び出しは正常に計測されましたが、Java からの呼び出しはインターセプトされませんでした。ふりだしに戻る。これが、アスペクトがバイトコード操作を使用する理由だと思います。

4

1 に答える 1

0

私はこれに対する答えを本当に理解していませんが、うまくいくと思う何かを思いつきました.

さて、スクリプトの実装が次のようになることを望んでいました。

@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persisteData
    // At this point persistedData contains data different depending on which user was passed in
...

クロージャーを使用すると、近いことができると思います。

@EventHandler
public void userEvent(UserInteractEvent event) {
    persistScope(event.user) {
        @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
        ...

そうすれば、persistScope 内で @Persist アノテーションのクロージャーをスキャンして、自分のことを行うことができます。クロージャーが開始されるまでその int が作成されていないため、これは正確には機能しない可能性がありますが、groovy から groovy に呼び出している限り、質問で言及した方法を使用して修正できると思います。それか、永続化されたユーザーデータを使用して「それ」をハッシュにします。

少し厄介ですが、うまくいくと思います。もう少し明示的であるという事実が気に入っています(実際、渡された「イベント」に .getUser() メソッドがあると仮定する前に、永続化のスコープを設定できるようになりました私が望むものに)。

これを実装して、これを受け入れる前に、私が尋ねた最初の質問に対する答えを誰かが思いつくかどうかを確認するために数日与えます.

編集:私はこの解決策に不満です。変数はそのスコープ内で宣言されているため、@Persist アノテーションを使用できませんでした。そのため、モジュールがデータ コンテナーとして使用できるハッシュを渡し、クロージャーが返された後にそれを永続化します。

まだより良い答えを探しています...

于 2012-04-23T03:24:46.450 に答える