如何应用、删除和重新应用 bytebuddy 转换?

How to apply, remove and re-apply the bytebuddy transformation?

我正尝试通过 javaagent apply, remove and re-apply bytebuddy 转换作为以下代码。删除通过 ResettableClassFileTransformer::reset 正常工作,但重新应用无效。

// The target classes

public @interface ToString {
}

public interface Greetable {
    String say(final String name);
}

@ToString
public class Greeter implements Greetable {
     public String say(final String name) {
         return "Hello " + name;
     }
}
//note the some transform is from the example
new AgentBuilder.Transformer() {
  @Override
  public DynamicType.Builder transform(DynamicType.Builder builder,
                                       TypeDescription     typeDescription,
                                       ClassLoader         classloader,
                                       JavaModule          module) {
    return builder.method(named("say"))
                  .intercept(FixedValue.value("transformed");
  }
})
// The transformation
public class MyProgram {
    public static void main(final String[] args) {
        ByteBuddyAgent.install();
        AgentBuilder bldr = new AgentBuilder.Default().
                                             with(AgentBuilder.Listener.
                                                      StreamWriting.
                                                      toSystemOut()).
                                             with(RedefinitionStrategy.
                                                     RETRANSFORMATION).
                                             disableClassFormatChanges().
                                             type(isSubTypeOf(
                                                       Greetable.class)).
                                             transform(<some transform>);
        ResettableClassFileTransformer resetter = 
                                          bldr.installOnByteBuddyAgent();
        Greetable g1 = new Greeter();
        System.out.println(g1.say("001"));

        //remove the transformation
        resetter.reset(ByteBuddyAgent.getInstrumentation(),
                       RedefinitionStrategy.RETRANSFORMATION);
        Greetable g2 = new Greeter();
        System.out.println(g2.say("002"));

        //re-apply the transformation
        bldr.installOn(ByteBuddyAgent.getInstrumentation());
        Greetable g3 = new Greeter();
        System.out.println(g3.say("003"));

    }
}

输出为

TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
                     null,
                     loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
                   null,
                   loaded=true]
transformed //note: the g1.say() is transformed.

Hello 002         //note: the g2 is an orginal

TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
                     null,
                     loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
                   null,
                   loaded=true]
Hello 003         //note the re-apply has not worked.

能否请您帮忙指教如何实现重新申请?

编辑1

我已将 byte-buddy 更新到版本 1.6.11 并更改了 AgentBuilder.Transformer。结果还是一样。 g3.say() 未转换。

我的环境是

Windows 10 64Bits

java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

编辑2

我已经从界面更改了类型匹配器

type(isSubTypeOf(Greetable.class))

具体class为

type(isSubTypeOf(Greeter.class))

g3.say()转化为属性。接口匹配转换有没有限制?

我从 AgentBuilder.Transformer API 可以看出,您是 运行 老版本的 Byte Buddy。我刚刚尝试使用最新版本并且它有效。

实际上,您是否依赖 @ToString 注释来触发转换?当您转换尚未加载的 class 时,字节好友会直接从 class 文件中读取注释。对于加载的 class,它使用加载的表示。您的注释未使用 @RetentionPolicy(Retention.RUNTIME) 进行注释以保留可能存在问题的注释可见性。

更新:您需要通过 not(isAbstract()).and(named("say")) 从接口中排除 say 方法来优化您的方法匹配器。否则,您将在重新转换尝试中包含该接口。 JVM 似乎因此而默默地失败了第二次重新转换,因为在加载 class 之后您无法将抽象方法更改为默认方法。通常,这会触发异常,但在您的情况下,VM 似乎会默默地抑制错误。