4

How can I read read the value of a Java method annotation at runtime with ASM ?
The Annotation has only a CLASS RetentionPolicy, so it's not possible to do that with Reflections.

| Policy CLASS: Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time

Example:
I want to extract the value Carly Rae Jepsen out of the artist field at runtime:

public class SampleClass {

    @MyAnnotation(artist = "Carly Rae Jepsen")
    public void callMeMaybe(){}
}
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface MyAnnotation {

    String artist() default "";
}

But why?
Can't you just change the RetentionPolicy to RUNTIME and do it with reflections?
In short: No. I use the modelmapper framework (Simple, Intelligent, Object Mapping). There I specify bidirectional mappings between Java classes by annotated methods. I wan't to reuse this information of hierarchical mappings, for change event propagation. But the provided mapstruct org.mapstruct.Mapping Annotation has CLASS RetentionPolicy. This is why I need to read that information out of the class files - and need ASM.

Tobse
  • 1,176
  • 1
  • 9
  • 22

2 Answers2

5

There are many samples which show the setup with asm and reading annotaions. But they don't showed it, for method annotations and how to also read the annotation value.

Minimal example how to do it:

import org.objectweb.asm.*;

public class AnnotationScanner extends ClassVisitor {
    public static void main(String[] args) throws Exception {
        ClassReader cr = new ClassReader(SampleClass.class.getCanonicalName());
        cr.accept(new AnnotationScanner(), 0);
    }

    public AnnotationScanner() {
        super(Opcodes.ASM8);
    }

    static class MyAnnotationVisitor extends AnnotationVisitor {
        MyAnnotationVisitor() {
            super(Opcodes.ASM8);
        }
        @Override
        public void visit(String name, Object value) {
            System.out.println("annotation: " + name + " = " + value);
            super.visit(name, value);
        }
    }

    static class MyMethodVisitor extends MethodVisitor {
        MyMethodVisitor() {
            super(Opcodes.ASM8);
        }
        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            System.out.println("annotation type: " + desc);
            return new MyAnnotationVisitor();
        }
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
                                     String signature, String[] exceptions) {
        System.out.println("method: name = " + name);
        return new MyMethodVisitor();
    }
}

Maven dependecy

<dependency>
  <groupId>org.ow2.asm</groupId>
  <artifactId>asm</artifactId>
  <version>8.0.1</version>
</dependency>

It will print:

method: name = callMeMaybe
annotation type: Lorg/springdot/sandbox/asm/simple/asm/MyAnnotation;
annotation: artist = Carly Rae Jepsen
Tobse
  • 1,176
  • 1
  • 9
  • 22
0

Usually People face issue when they want to read array values in an Annotation.

Because to read array Annotation we have to use visitArray Method and visitArray calls visit method internally to read values. Sharing below code how can you achieve. Best practice to use switch under visit & visitArray Method

Suppose there is a test class, interface Group is defined but we have to read values via visitArray method of AnnotationVisitor class

@Tid(value = "1234")
@Group(value = {"G1","G2"})
Class A {
}

=====================================Solution ========================

public AnnotationVisitor visitAnnotation(String desc){

if(desc = Attr.Tid) new AnnotationVisitor(Opcodes.ASM9){
        String id = "";
      @Override 
      public void visit(String name, Object v ){
        id = v;
        }

      @Override
      public void visitEnd(){
     
        }
  }  //if statement closes

 else if(desc == Attr.Group) new AnnotationVisitor(Opcodes.ASM9){
   //declare Set or ArrayList e.g. storeGroupValue
   @Override
  public AnnotationVisitor visitArray(String name){

    if(name.equals("Group")){ new AnnotationVisitor(Opcodes.ASM9) {
   @Override
    public void visit(String name, Object value){
    storeGroupValue.add(value)    --> this will store G1, G2 values
    } //visit method 
   } //Av Object
  } // if condition
 else{
  super.visitArray(name) }

}// close visitArray method

=== write visitEnd of visitArray=====

}  //else if to validate Group Annotation

} //close visitAnnotation
Procrastinator
  • 2,526
  • 30
  • 27
  • 36