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 のバグですか、それともこの場合、実際に「メソッド」パラメーターを使用する必要がありますか?