注解
如果类,字段,方法和方法参数注解(例如@Deprecated或@Override)存储在已编译的类中,则它们的保留策略不是RetentionPolicy.SOURCE。
该信息在运行时不会由字节码指令使用,但是如果保留策略为RetentionPolicy.RUNTIME,则可以通过反射API进行访问。
编译器也可以使用它。
结构
结构体
源代码中的注解可以采用多种形式,例如@ Deprecated,@ Retention(RetentionPolicy.CLASS)或@Task(desc =“ refactor”,id = 1)。
但是,在内部,所有注解都具有相同的形式,并由注解类型和一组名称/值对指定,其中值限于:
- 
    基本,字符串或类值, 
- 
    枚举值, 
- 
    注解值, 
- 
    以上值的数组。 
请注意,注解可以包含其他注解,甚至可以包含注解数组。
因此,注解可能非常复杂。
Interfaces and components
用于生成和转换注解的ASM API基于AnnotationVisitor抽象类(请参见图4.3)。
- Figure 4.3.: The AnnotationVisitor class
public abstract class AnnotationVisitor {
    public AnnotationVisitor(int api);
    public AnnotationVisitor(int api, AnnotationVisitor av);
    public void visit(String name, Object value);
    public void visitEnum(String name, String desc, String value);
    public AnnotationVisitor visitAnnotation(String name, String desc);
    public AnnotationVisitor visitArray(String name);
    public void visitEnd();
}
此类的方法用于访问注解的名称值对(在返回此类型的方法(即visitAnnotation方法)中访问注解类型)。
第一种方法用于原始值,String和Class值(后一种由Type对象表示),其他方法用于枚举,注解和数组值。
可以按任何顺序调用它们,但visitEnd除外:
( visit | visitEnum | visitAnnotation | visitArray )* visitEnd
请注意,有两个方法返回AnnotationVisitor:这是因为注解可以包含其他注解。
也不同于ClassVisitor返回的MethodVisitors,这两个方法返回的AnnotationVisitors必须顺序使用:
实际上,在完全访问嵌套注解之前,不必调用父访问者的方法。
还要注意,visitArray方法将AnnotationVisitor返回到访问数组的元素。
但是,由于未命名数组的元素,所以visitArray返回的访问者的方法将忽略name参数,并且可以将其设置为null。
添加,删除和检测注解
删除
像字段和方法一样,可以通过在visitAnnotation方法中返回null来删除注解:
public class RemoveAnnotationAdapter extends ClassVisitor {
    private String annDesc;
    public RemoveAnnotationAdapter(ClassVisitor cv, String annDesc) {
        super(ASM4, cv);
        this.annDesc = annDesc;
    }
    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean vis) {
        if (desc.equals(annDesc)) {
            return null;
        }
        return cv.visitAnnotation(desc, vis);
    }
}
增加
由于必须调用ClassVisitor类的方法的限制,添加类注解会更加困难。
实际上,必须重写visitAnnotation之后的所有方法,以检测何时访问了所有注解(由于使用了visitCode方法,方法注解更易于添加):
public class AddAnnotationAdapter extends ClassVisitor {
    private String annotationDesc;
    private boolean isAnnotationPresent;
    public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) {
        super(ASM4, cv);
        this.annotationDesc = annotationDesc;
    }
    @Override 
    public void visit(int version, int access, String name,
        String signature, String superName, String[] interfaces) {
        int v = (version & 0xFF) < V1_5 ? V1_5 : version;
        cv.visit(v, access, name, signature, superName, interfaces);
    }
    @Override 
    public AnnotationVisitor visitAnnotation(String desc,
        boolean visible) {
        if (visible && desc.equals(annotationDesc)) {
            isAnnotationPresent = true;
        }
        return cv.visitAnnotation(desc, visible);
    }
    @Override 
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        addAnnotation();
        cv.visitInnerClass(name, outerName, innerName, access);
    }
    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        addAnnotation();
        return cv.visitField(access, name, desc, signature, value);
    }
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        addAnnotation();
        return cv.visitMethod(access, name, desc, signature, exceptions);
    }
    @Override public void visitEnd() {
        addAnnotation();
        cv.visitEnd();
    }
    
    private void addAnnotation() {
        if (!isAnnotationPresent) {
            AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true);
            if (av != null) {
                av.visitEnd();
            }
            isAnnotationPresent = true;
        }
    }
}
请注意,如果该适配器的版本低于该版本,则它将升级到1.5。
这是必需的,因为JVM会忽略版本小于1.5的类中的注解。
在类和方法适配器中,注解的最后一个(也是最常见的)用例是使用注解以参数化转换。
例如,您可以仅对以下字段进行字段访问转换:
具有 @Persistent 批注,仅将日志记录代码添加到具有 @Log 批注的方法中,依此类推。
所有这些用例都可以轻松实现,因为必须首先访问注解:必须在字段和方法之前访问类注解,并且必须在代码之前访问方法和参数注解。
因此,只要在检测到所需注解时设置一个标志,然后在转换中稍后使用它就足够了,就像上述示例中使用isAnnotationPresent标志所做的那样。
工具类
TraceClassVisitor,CheckClassAdapter和ASMifier类, 在第2.3节中发送的消息也支持注解(与方法一样,也可以使用TraceAnnotationVisitor或CheckAnnotationAdapter在单个注解级别而不是在类级别使用)。
它们可以用来查看如何生成一些特定的注解。
例如使用:
java -classpath asm.jar:asm-util.jar \
org.objectweb.asm.util.ASMifier \
java.lang.Deprecated
打印经过少量重构后的代码,其内容为:
package asm.java.lang;
import org.objectweb.asm.*;
public class DeprecatedDump implements Opcodes {
    public static byte[] dump() throws Exception {
    ClassWriter cw = new ClassWriter(0);
    AnnotationVisitor av;
    cw.visit(V1_5, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT
    + ACC_INTERFACE, "java/lang/Deprecated", null,
    "java/lang/Object",
    new String[] { "java/lang/annotation/Annotation" });
        {
        av = cw.visitAnnotation("Ljava/lang/annotation/Documented;",
        true);
        av.visitEnd();
        }
        {
        av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
        av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
        "RUNTIME");
        av.visitEnd();
        }
        cw.visitEnd();
        return cw.toByteArray();
    }
}
这段代码显示了两个如何使用ACC_ANNOTATION标志创建注解类,并显示了如何创建两个类注解,一个没有值,一个带有枚举值。 可以用类似的方法来创建方法和参数注解,方法是在MethodVisitor类中定义了visitAnnotation和visitParameterAnnotation方法。
