使用 ByteBuddy 向方法添加行为

add behavior to method with ByteBuddy

给定 Sample.someMethod(...)InvocationHandler decoration,我想创建一个用

覆盖 someMethod 的动态子类
someMethod(...) {
  decoration.invoke(...);
  super.someMethod(...);
}

我的代码如下所示:

    Class<? extends Sample> dynamicSubclass = new ByteBuddy()
        .subclass(Sample.class)
        .method(ElementMatchers.named("someMethod"))
            .intercept(new Implementation.Compound(
                InvocationHandlerAdapter.of(decoration),
                SuperMethodCall.INSTANCE))
        .make()
        .load(Sample.class.getClassLoader())  // line 42
        .getLoaded();

...并导致以下异常:

java.lang.VerifyError: Expecting a stack map frame
Exception Details:
  Location:
    pl/morgwai/sample/pojo/ByteBuddySample$Sample$ByteBuddy$PXUw8Sz7.someMethod(I)Ljava/lang/String; @27: aload_0
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: b200 0a2a b200 0e04 bd00 1059 031b b800
    0000010: 1653 b900 1c04 00c0 001e b02a 1bb7 0020
    0000020: b0                                     

    at java.base/java.lang.Class.getDeclaredFields0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3061)
    at java.base/java.lang.Class.getDeclaredField(Class.java:2409)
    at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:122)
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:192)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:102)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6292)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6281)
    at pl.morgwai.sample.pojo.ByteBuddySample.main(ByteBuddySample.java:42)

我做错了什么以及如何解决?
(我使用的是 byte-buddy 1.10.22 和 openjdk-11)

谢谢!

更新:

我能做到

someMethod(...) {
  decoration.invoke(...);
}

someMethod(...) {
  super.someMethod(...);
}

通过分别执行 .intercept(InvocationHandlerAdapter.of(decoration)).intercept(SuperMethodCall.INSTANCE),看来,我没有正确使用 Implementation.Compound...

更新 2:

我也能做到

someMethod(...) {
  super.someMethod(...);
  decoration.invoke(...);
}

来自

.intercept(SuperMethodCall.INSTANCE.andThen(InvocationHandlerAdapter.of(decoration)))

但我不能用其他方法来实现我需要的顺序,因为 InvocationHandlerAdapter 没有实现 Implementation.Composable...

explained by Rafael Winterhalter it's an unintended omission and in the next release InvocationHandlerAdapter will be implementing Composable (commit 已推送)然后最简单的方法就是

.intercept(InvocationHandlerAdapter.of(decoration).andThen(SuperMethodCall.INSTANCE))