如何使用ByteBuddy读取Java类方法的注解。

huangapple go评论79阅读模式
英文:

How to read a Java class method annotation with ByteBuddy

问题

如何在运行时使用 ByteBuddy 读取 Java 类方法的注解?

示例:
我正在使用 ByteBuddy 来跟踪方法的进入和退出。在执行此操作时,我需要通过读取 @Test 注解来判断一个方法是否为单元测试方法。

  @Test
  public void getBuildByAuthor() {
    .
    .
  }

以下是我如何记录方法的进入/退出的示例。

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: 是否是由 @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);
  }

更新:
我通过为带有 @Test 注解的方法创建单独的 Advice 来解决了这个问题,如下所示:

  @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);
  }
英文:

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(&quot;#t&quot;) String type,
@Advice.Origin(&quot;#m&quot;) String method,
@Advice.Origin(&quot;#s&quot;) 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(&quot;com.examples.&quot;)) 
.transform(new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder&lt;?&gt; transform(DynamicType.Builder&lt;?&gt; 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(&quot;com.examples.&quot;)) 
.transform(new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder&lt;?&gt; transform(DynamicType.Builder&lt;?&gt; 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);
}

答案1

得分: 1

我理解您希望从您的建议中读取一个注解?您需要注册一个自定义绑定,将该值生成为其结果。 Advice 通过以下配置实现了这一点:

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

您需要做的就是自己定义一个注解,并为其设置 Retention.RUNTIME,然后使用该注解对您希望表示自定义值的参数进行注释。在绑定期间提供的绑定器负责解析此值,例如返回一个布尔值,指示方法是否被注解以及使用了哪些值。

如果您只想对带有特定注解的方法执行操作,那么只匹配带有注解的方法以应用该建议将更加高效。

英文:

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.

huangapple
  • 本文由 发表于 2020年10月11日 09:27:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/64299837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定