当前位置: 首页 > 知识库问答 >
问题:

使用Byte-Buddy拦截java.lang.Object wait(long)本机方法

彭仲卿
2023-03-14

我正在尝试记录“object.wait”方法调用的计数。截取“public final native void wait(long timeout)”对ByteBuddy无效。我做了一些测试:

test1,test2打印错误:java.lang.IllegalStateException:无法为公共最终void java.lang.Object.Wait(long,int)调用超级(或默认)方法抛出java.lang.InterruptedException;java.lang.IllegalStateException:无法为公共最终本机void java.lang.Object.Wait(长)抛出java.lang.InterruptedException调用超级(或默认)方法

test3无效,不打印任何内容。

测试4为成功。

下面是我的测试代码:


final public class AgentBootstrap {

     public static class TestAdvice {
        @Advice.OnMethodEnter
        public static void before(@Advice.Origin String methodIns) {
            System.out.println("Byte-Buddy enter:" + methodIns);
        }

        @Advice.OnMethodExit
        public static void after(@Advice.Origin String methodIns) {
            System.out.println("Byte-Buddy after:" + methodIns);
        }
      }
    
     public static void premain(String agentArgs, Instrumentation inst) throws Exception{
         AgentBuilder agentBuilder = new AgentBuilder.Default()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                .with(AgentBuilder.TypeStrategy.Default.REBASE)
                .enableNativeMethodPrefix("$$mynative_")
                .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly())
                .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
                .ignore(nameStartsWith("net.bytebuddy."));
        // test1(agentBuilder, inst);
        // test2(agentBuilder, inst);
        // test3(agentBuilder, inst);
        // test4(agentBuilder, inst);
        
    }
     
    /**
     * intercept method: public final native void wait(long timeout)
     * print error: java.lang.IllegalStateException: Cannot call super (or default) method for public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
     */
    private static void test1(AgentBuilder agentBuilder, Instrumentation inst) throws Exception {
        agentBuilder.disableClassFormatChanges()
        .type(is(Object.class))
        .transform(new Transformer() {
            @Override
            public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader,JavaModule module) {
              return builder.method(named("wait").and(takesArguments(1))).intercept(Advice.to(TestAdvice.class));
            }
        })
        .installOn(inst);
        inst.retransformClasses(Object.class);
    }
    /**
     * intercept method: public final void wait(long timeout, int nanos)
     * print error: java.lang.IllegalStateException: Cannot call super (or default) method for public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
     */
    private static void test2(AgentBuilder agentBuilder, Instrumentation inst) throws Exception {
        agentBuilder.disableClassFormatChanges()
        .type(is(Object.class))
        .transform(new Transformer() {
            @Override
            public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader,JavaModule module) {
              return builder.method(named("wait").and(takesArguments(2))).intercept(Advice.to(TestAdvice.class));
            }
        })
        .installOn(inst);
        inst.retransformClasses(Object.class);
    }
    
    /**
     * intercept method: public final native void wait(long timeout)
     * invalid, print nothing
     */
    private static void test3(AgentBuilder agentBuilder, Instrumentation inst) throws Exception {
        agentBuilder.disableClassFormatChanges()
        .type(is(Object.class))
        .transform(new Transformer() {
            @Override
            public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader,JavaModule module) {
                return builder.visit(Advice.to(TestAdvice.class).on(named("wait").and(takesArguments(1))));
            }
        })
        .installOn(inst);
        inst.retransformClasses(Object.class);
    }
    /**
     * intercept method: public final void wait(long timeout, int nanos)
     * success
     */
    private static void test4(AgentBuilder agentBuilder, Instrumentation inst) throws Exception {
        agentBuilder.type(is(Object.class))
        .transform(new Transformer() {
            @Override
            public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader,JavaModule module) {
                return builder.visit(Advice.to(TestAdvice.class).on(named("wait").and(takesArguments(2))));
            }
        })
        .installOn(inst);
    }
     
}
public static void main(String[] args) throws Exception {
    new Thread(() -> {
        Object obj = new Object();
        while (true){
            try {
                synchronized (obj) {
                    obj.wait(1000);
                    obj.wait(1000, 1);
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

:(Test4代码是针对“public final void wait()”方法的,它起作用了,但是对于“public final native void wait()”方法却不起作用。我想知道如何拦截“public final native void wait()”方法。

共有1个答案

米修平
2023-03-14

研究一下这个问题,实际上Byte Buddy版本1.10.14中存在一个bug。重新基于的方法必须具有修饰符private final而不是仅仅private,否则,JVM将不接受重新基于的方法。

修复程序已经在master上了,如果您自己构建了Byte Buddy,您现在应该可以运行代理了。下一个版本1.10.15将包含此修补程序。

但是,请注意,JVM不再支持从版本13开始添加私有(final)方法。可以使用-xx:+AllowRedefinitionToAddDeleteMethods重置旧的行为。但是,该选项不推荐使用,并且在JVM的未来版本中将不再存在。如果没有这种重命名本机方法的可能性,那么在JVM上,无论有没有Byte buddy,这种行为都是不可能的。

(透露一下,我是Byte Buddy的维护者。)

 类似资料:
  • 我想改变为任意 Java 程序调用 时会发生什么(我想将时间移回以帮助容器检查点/恢复用例)。每当调用System.nanoTime()时,我都想运行原始然后可能返回一个不同的值。我正在尝试使用字节伙伴和 JVM 仪表代理来执行此操作(根据此处和此处)。 这是我的尝试: 和 但这失败了: 有什么办法可以实现这一点?我试过的其他东西: 使用 而不是 然后在 nanoTime() 中调用 - 这似乎是

  • 问题内容: 我一直在寻找“如何在运行时向方法添加注释”的答案,发现了一个名为Byte Buddy的出色工具,可以使用它,但仍然无法按需使用。我确定它必须能够从这个问题做到这一点Byte Buddy可以在运行时创建字段和方法注释吗? 上这堂课: 和此代码: 向 类 添加注释很容易。但是对于 方法而言 ,似乎不更改方法实现是不可能的。 我敢肯定,我只是做得不好,但是不幸的是,当方法 没有代码更改 而只

  • 问题内容: 我有这个代码 有什么方法可以在没有子类化或修改类且没有工厂的情况下拦截呼叫? 编辑:抱歉忘了提到这是在Android平台上。 问题答案: 您是否考虑过面向方面的编程,甚至还考虑过AspectJ?有关AspectJ / Android的信息,请参见此处和此处。

  • 我正在使用spring AOP拦截这些方法。我在spring配置文件中有以下配置。 方面类: 上面的方法不拦截私有方法?要求方面既拦截私有方法,又拦截公有方法,怎么办?

  • 问题内容: 我正在使用Java EE 6和Jboss AS7.1,并尝试使用拦截器绑定(来自jboss网站的示例)。 我有一个InterceptorBinding注解: 拦截器: 还有一个豆: 但是拦截器没有被称为。。。 在编写此代码时将调用拦截器: 谢谢你的帮助。 问题答案: 您是否按照参考示例中的说明启用了拦截器? 缺省情况下,bean档案没有通过拦截器绑定绑定的已启用拦截器。必须通过将侦听器

  • 问题很简单 在这里打破头! 编辑:一个小突破。我打印了目标,它返回的是SimpleJPrepository,而不是实际的存储库。