2

Byte Buddy (v0.5.2) を使用して、インターフェイスの「サブクラス」を動的に作成します (実際には、そのインターフェイスを実装するクラスを作成したいと考えています)。このクラスのインスタンスで呼び出されるすべてのメソッドは、別の (インターセプター) クラスにリダイレクトする必要があります。次のコードを使用しました (「TestInterface」は、「sayHello」メソッドを 1 つだけ宣言するインターフェイスです)。

final Interceptor interceptor = new Interceptor();
Class<?> clazz = new ByteBuddy()
        .subclass(TestInterface.class)
        .method(any()).intercept(MethodDelegation.to(interceptor))
        .make()
        .load(TestInterface.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
        .getLoaded();
TestInterface instance = (TestInterface) clazz.newInstance();
instance.sayHello();

インターセプター クラスは次のようになります。

public class Interceptor {

    public Object intercept(@Origin MethodHandle method, @AllArguments Object[] args) throws Throwable {
        ...
    }       

}

ただし、「sayHello」メソッド (コード例の最後の行) を呼び出そうとすると、「IncompatibleClassChangeError」が発生します。スタック トレースは次のとおりです。

Exception in thread "main" java.lang.IllegalAccessError: no such method: byteuddytest.TestInterface.sayHello()void/invokeVirtual
    at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:448)
    at bytebuddytest.TestInterface$ByteBuddy$0E9xusGs.sayHello(Unknown Source)
    at bytebuddytest.Main.main(Main.java:32)
Caused by: java.lang.IncompatibleClassChangeError: Found interface bytebuddytest.TestInterface, but class was expected
    at java.lang.invoke.MethodHandleNatives.resolve(Native Method)
    at java.lang.invoke.MemberName$Factory.resolve(MemberName.java:965)
    at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:990)
    at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1387)
    at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1732)
    at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:442)
... 2 more

この問題は、インターセプター メソッドでの「MethodHandle」パラメーターの使用に関連しているようです。タイプを「メソッド」に変更すると、すべて正常に動作します。ただし、ドキュメントによると、パフォーマンス上の理由から、「メソッド」よりも「メソッドハンドル」を優先する必要があります。

エラーの原因は Byte Buddy のバグですか、それともこの場合、実際に「メソッド」パラメーターを使用する必要がありますか?

4

2 に答える 2

1

完全に正しいJeorの回答を参照してください(承認済みとしてマークする必要があります)。コメントに収まらない 2 つのコメント:

もちろん、 a のMethodHandle代わりに a を使用する必要があるのMethodは、前者で許可されている場合のみです。sMethodHandleの呼び出しは、何らかの JVM マジックを意味します。ハンドルは、JVM によってポリモーフィック シグネチャで解決されます。つまり、JVM は単に呼び出しサイトをメソッド呼び出しに置き換えるため、ハンドルの引数をボックス化してはなりません。したがって、あなたの場合、これは機能しません。ただし、メソッド ハンドルの利点は、クラスの定数プールに格納できることです。バイトコード命令でアクセスできるネイティブな概念です。それに比べて、Method参照は明示的に作成する必要があります。

したがって、インスタンスをキャッシュする必要がありMethodます (これは変更可能です!)。また、現在、 のメソッドもインターセプトしていることに注意してくださいObject。次の方法で、コードを少しクリーンアップできます。

Class<? extends TestInterface> clazz = new ByteBuddy()
        .subclass(TestInterface.class)
        .method(isDeclaredBy(TestInterface.class))
        .intercept(MethodDelegation.to(interceptor))
        .make()
        .load(TestInterface.class.getClassLoader(), 
              ClassLoadingStrategy.Default.INJECTION)
        .getLoaded();

TestInterface instance = clazz.newInstance();
于 2015-01-23T07:30:04.877 に答える