【问题标题】:Intercepting Object.class toString() method with Byte Buddy使用 Byte Buddy 拦截 Object.class toString() 方法
【发布时间】:2020-09-01 13:31:10
【问题描述】:

我正在使用 Byte Buddy 拦截一些 JDK 方法,System.class 和 Thread.class 很好,但不适用于 java.lang.Object。我在 JDK8 上运行我的测试,它没有抛出任何错误。

final public class AgentBootstrap {
  public static void premain(String agentArgs, Instrumentation inst) throws Exception {
    try {
      new AgentBuilder.Default()
        .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
        .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
        .with(AgentBuilder.TypeStrategy.Default.REBASE)
        .enableNativeMethodPrefix("$$mynative_")
        .ignore(ElementMatchers.none())
        .with(
          new AgentBuilder.Listener.Filtering(
            new StringMatcher("java.lang.Object", StringMatcher.Mode.EQUALS_FULLY),
            AgentBuilder.Listener.StreamWriting.toSystemOut()))
        .type(ElementMatchers.is(Object.class))
        .transform(new Transformer() {
          @Override
          public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
            return builder.method(ElementMatchers.named("toString")).intercept(FixedValue.value("HELLO BYTE BUDDY!"));
          }
        })
        .installOn(inst);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

并且我尝试使用Javassist,变换java.lang.Object的方法是成功的。 谁能知道为什么它不适用于 Object.class?

【问题讨论】:

    标签: byte-buddy


    【解决方案1】:

    你想用

    • disableClassFormatChanges() 为了避免结构类文件更改会违反重新转换规则,
    • advice API 用于将代码插入现有方法而不添加新方法。
    import net.bytebuddy.agent.ByteBuddyAgent;
    import net.bytebuddy.agent.builder.AgentBuilder;
    import net.bytebuddy.asm.Advice;
    
    import java.lang.instrument.Instrumentation;
    
    import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.RETRANSFORMATION;
    import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;
    import static net.bytebuddy.matcher.ElementMatchers.*;
    
    class Scratch {
    
      public static class ToStringAdvice {
        @Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class)
        public static boolean before() {
          // Skip original method execution (false is the default value for boolean)
          return false;
        }
    
        @Advice.OnMethodExit
        public static void after(@Advice.Return(readOnly = false, typing = DYNAMIC) Object returnValue) {
          // Set fixed return value
          returnValue = "HELLO BYTE BUDDY!";
        }
      }
    
      public static void premain(String agentArgs, Instrumentation inst) {
        new AgentBuilder.Default()
          .disableClassFormatChanges()
          .with(RETRANSFORMATION)
          .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
          .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
          .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
          .ignore(none())
          .type(is(Object.class))
          .transform((builder, typeDescription, classLoader, module) ->
            builder.visit(
              Advice
                .to(ToStringAdvice.class)
                .on(named("toString"))
            )
          )
          .installOn(inst);
      }
    
      public static void main(String[] args) throws Exception {
        Instrumentation instrumentation = ByteBuddyAgent.install();
        premain("", instrumentation);
        instrumentation.retransformClasses(Object.class);
        System.out.println(new Object());
      }
    
    }
    

    这行得通,...

    [Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@3bfdc050 on sun.instrument.InstrumentationImpl@1bce4f0a
    [Byte Buddy] REDEFINE BATCH #0 [1 of 1 type(s)]
    [Byte Buddy] TRANSFORM java.lang.Object [null, null, loaded=true]
    [Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 1 types [0 failed batch(es)]
    [Byte Buddy] INSTALL HELLO BYTE BUDDY! on HELLO BYTE BUDDY!
    [Byte Buddy] TRANSFORM java.lang.Object [null, null, loaded=true]
    HELLO BYTE BUDDY!
    

    ...但我认为在操作 JDK 核心类之前应该三思而后行。看上面的日志。你注意到日志行是怎么回事

    [Byte Buddy] INSTALL HELLO BYTE BUDDY! on HELLO BYTE BUDDY!
    

    正在打印两个显然使用您刚才建议的方法的对象?所以要小心!

    【讨论】:

    • 谢谢,它有效。我想我想使用“builder.visit”。还有一个问题,这些代码对“Object.wait()”不起作用,我要记录计数,可行吗?
    • 当然它对wait() 不起作用,因为advice 旨在为toString() 返回一个固定值,同时也阻止了对原始方法的调用。只需为wait() 创建一个类似的建议,但让原始方法通过,因为您不想更改功能。在这种情况下,您只需要一个进入或退出通知,计算调用而不做其他任何事情,具体取决于您是要在等待之前还是之后计算调用。很简单。
    • 嗯,再想想,advice 方法体将不得不更新位于主类或静态内部advice 类中某处的计数器。因为类Object 驻留在引导类加载器上,所以您必须将从那里调用的类注入到同一个类加载器中。这使它变得更加复杂,并且绝对证明了一个新问题的合理性。所以请创建一个提到这个的,并在此处通过评论通知我,然后我可以查看并单独回答新问题。这将是题外话。
    • 这是一个新问题link
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-07
    相关资源
    最近更新 更多