简体   繁体   中英

How to read a Java class method annotation with ByteBuddy

How can I read Java class method annotation at runtime with ByteBuddy?

Example: I am using bytebuddy to track method enter and exit. While doing this, I need to know whether a method is unit test by reading the annotation @Test.

  @Test
  public void getBuildByAuthor() {
    .
    .
  }

Here is a sample on how I am recording enter/exit of methods.

public class MethodTracer {

  @Advice.OnMethodEnter(inline = false)
  public static Item enter(@Advice.Origin("#t") String type,
                                @Advice.Origin("#m") String method,
                                @Advice.Origin("#s") String signature)
  {
    //TODO: is this Unit test method identified by @Test ?
    return Tracer.enter(type, method, signature);
  }

  @Advice.OnMethodExit(inline = false, onThrowable = Throwable.class)
  public static void exit(@Advice.Enter Item item) {
    Tracer.exit(item);
  }
}
  @Override
  public void instrument(Instrumentation instrumentation) {
    final Advice methodAdvice = Advice.to(MethodTracer.class);
    final Advice constructorAdvice = Advice.to(ConstructorTracer.class);


    ResettableClassFileTransformer agent = new AgentBuilder.Default()
        .with(new TracerLogger())  
        .type(ElementMatchers.nameStartsWith("com.examples.")) 
        .transform(new AgentBuilder.Transformer() {
          @Override
          public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
            builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods().method(ElementMatchers.isMethod(), methodAdvice));
            builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods().constructor(ElementMatchers.isConstructor(), constructorAdvice));
            return builder;
          }
        })
        .installOn(instrumentation);
  }

Update: I solved the problem by creating separate Advice for methods with annotation @Test as follows:

  @Override
  public void instrument(Instrumentation instrumentation) {
    final Advice methodAdvice = Advice.to(MethodTracer.class);
    final Advice testMethodAdvice = Advice.to(TestMethodTracer.class);
    final Advice constructorAdvice = Advice.to(ConstructorTracer.class);
    final Advice testConstructorAdvice = Advice.to(TestConstructorTracer.class);


    ResettableClassFileTransformer agent = new AgentBuilder.Default()
        .with(new TracerLogger())
            .type(ElementMatchers.nameStartsWith("com.examples.")) 
            .transform(new AgentBuilder.Transformer() {
          @Override
          public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
              builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods().method(ElementMatchers.isMethod().and(ElementMatchers.isAnnotatedWith(Test.class)), testMethodAdvice));
              builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods().method(ElementMatchers.isMethod().and(ElementMatchers.not(ElementMatchers.isAnnotatedWith(Test.class))), methodAdvice));
              builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods().constructor(ElementMatchers.isConstructor().and(ElementMatchers.isAnnotatedWith(Test.class)), testConstructorAdvice));
              builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods().constructor(ElementMatchers.isConstructor().and(ElementMatchers.not(ElementMatchers.isAnnotatedWith(Test.class))), constructorAdvice));

              return builder;
          }
        })
        .installOn(instrumentation);
  }

I understand it that you would like to read an annotation from your advice? You'd need to register a custom binding that produces this value as its result. Advice allows this by a configuration:

Advice.withCustomMapping().bind(MyAnnotation.class, ...).to(...)

All you need to do is to define an annotation yourself, set Retention.RUNTIME for it and annotate the parameter you'd like to represent your custom value with it. The binder that is supplied during the binding is then responsible to resolve this value, for example to return a boolean that indicates if the method is annotated and with what values.

If you only want to do things for methods with a given annotation, it would however be more efficient to only match methods with the annotations to apply the advice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM