使用byte buddy API进行方法参数的Prtining

时间:2017-04-17 07:32:08

标签: bytecode-manipulation byte-buddy

我正在开发一个项目,我需要在执行期间访问方法参数。 是否可以使用字节伙伴框架打印方法参数?任何使用javaagent的示例代码都非常感谢。

2 个答案:

答案 0 :(得分:0)

是的,这是可能的。您可以使用MethodDelegationAdvice注入代码,然后使用@AllArguments注释来获取实际参数。

问题是,如何在项目中创建代码?您可以使用带有AgentBuilder的Java代理,也可以使用ByteBuddy实例创建代理子类。请参阅文档和提到的类javadoc以了解如何完成此操作。

答案 1 :(得分:0)

这里是一个如何使用MethodDelegation来实现的示例。我用它来衡量方法的执行时间。我特别没有开始删除多余的代码,因为我想更全面地揭示Byte Buddy的功能。

    package md.leonis.shingler;

    import net.bytebuddy.agent.ByteBuddyAgent;
    import net.bytebuddy.agent.builder.AgentBuilder;
    import net.bytebuddy.implementation.MethodDelegation;
    import net.bytebuddy.implementation.bind.annotation.AllArguments;
    import net.bytebuddy.implementation.bind.annotation.Origin;
    import net.bytebuddy.implementation.bind.annotation.RuntimeType;
    import net.bytebuddy.implementation.bind.annotation.SuperCall;
    import net.bytebuddy.matcher.ElementMatchers;

    import java.lang.instrument.Instrumentation;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.concurrent.Callable;
    import java.util.stream.Collectors;

    public class MeasureMethodTest {

        public static void main(String[] args) throws InterruptedException {
            premain(ByteBuddyAgent.install());
            for (int i = 0; i < 4; i++) {
                SampleClass.foo("arg" + i);
            }
        }

        public static void premain(Instrumentation instrumentation) {
            new AgentBuilder.Default()
                    .type(ElementMatchers.nameStartsWith("md.leonis.shingler"))
                    .transform((builder, type, classLoader, module) ->
                            builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(AccessInterceptor.class))
                    ).installOn(instrumentation);
        }

        public static class AccessInterceptor {

            @RuntimeType
            public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable, @AllArguments Object[] args) throws Exception {
                long start = System.nanoTime();
                try {
                    return callable.call();
                } finally {
                    if (method.getAnnotationsByType(Measured.class).length > 0) {
                        String params = Arrays.stream(args).map(Object::toString).collect(Collectors.joining(", "));
                        System.out.println(method.getReturnType().getSimpleName() + " " + method.getName() + "("+ params +") took " + ((System.nanoTime() - start) / 1000000) + " ms");
                    }
                }
            }
        }

        public static class SampleClass {
            @Measured
            static void foo(String s) throws InterruptedException {
                Thread.sleep(50);
            }
        }
    }

此示例测量了md.leonis.shingler程序包中找到并标记为@Measured的所有方法的执行时间。

要运行它,您需要两个库:byte-buddybyte-buddy-agent

工作结果:

void foo(arg0) took 95 ms
void foo(arg1) took 50 ms
void foo(arg2) took 50 ms
void foo(arg3) took 50 ms

请注意,控制台将显示传递给该方法的所有参数的值。这是所提问题的答案。

这是注释示例:

    package md.leonis.shingler;

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Measured {

    }

说实话,我无法直接在代理中通过注释配置筛选。这是一个示例(不起作用):

    new AgentBuilder.Default()
                    .type(ElementMatchers.isAnnotatedWith(Measured.class))
                    .transform((builder, type, classLoader, module) ->
                            builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(AccessInterceptor.class))
                    ).installOn(instrumentation);

如果有人知道该怎么做,请在下面发表评论。