Java注解解析

120 阅读5分钟

Annotation

注解实现

我们知道注解有一个存活时间,有3类,这3类都是有一个Enum实现的 RetentionPolicy

/**
 * TestAnnotation retention policy.  The constants of this enumerated type
 * describe the various policies for retaining annotations.  They are used
 * in conjunction with the {@link Retention} meta-annotation type to specify
 * how long annotations are to be retained.
 *
 * @author Joshua Bloch
 * @since 1.5
 */
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    //编译器可见(也就是可以用于apt)
    //不会写入到class字节码里面
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    //写入到class文件里面.但是运行期间不可见
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    //运行期也可以获取
    RUNTIME
}

SOURCE & CLASS

这方面的实现笔者实力有限不能进行分析,这得去看javac(java compiler)

不过我们可以验证一下


@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    byte a() default 0;

    short b() default 0;

    int c() default 0;

    long d() default 0;

    double e() default 0;

    float f() default 0;

    boolean g() default false;

    String h() default "";

    Class<?> i() default Object.class;
}

@MyAnnotation()
public class TestAnnotation {

    public static void main(String[] args) {
        System.out.println(Arrays.toString(TestAnnotation.class.getAnnotations()));
    }

}

完全没有annotation的标记

img.png

修改为@Retention(RetentionPolicy.CLASS)

img_1.png 可以发现代码的注释其实是给的很清楚的,没有骗我们.

RUNTIME

什么是注解?

JVM并没有为注解定义新的类型,所以注解会是什么? 这个嘛看看字节码就知道了


@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    byte a() default 0;

    short b() default 0;

    int c() default 0;

    long d() default 0;

    double e() default 0;

    float f() default 0;

    boolean g() default false;

    String h() default "";

    Class<?> i() default Object.class;
}

分析一下字节码 img_2.png

晴天霹雳有没有? 注解底层在javac的蹂躏下变成了一个interface

对比一下interface

public interface MyInterface {
    short a();

    int b();
}

img_3.png

差别不大就是annotation多了一个名为java.lang.annotation.Annotation的父类 看看就好了

public interface Annotation {
    /**
     * Returns true if the specified object represents an annotation
     * that is logically equivalent to this one.  In other words,
     * returns true if the specified object is an instance of the same
     * annotation type as this instance, all of whose members are equal
     * to the corresponding member of this annotation, as defined below:
     * <ul>
     *    <li>Two corresponding primitive typed members whose values are
     *    {@code x} and {@code y} are considered equal if {@code x == y},
     *    unless their type is {@code float} or {@code double}.
     *
     *    <li>Two corresponding {@code float} members whose values
     *    are {@code x} and {@code y} are considered equal if
     *    {@code Float.valueOf(x).equals(Float.valueOf(y))}.
     *    (Unlike the {@code ==} operator, NaN is considered equal
     *    to itself, and {@code 0.0f} unequal to {@code -0.0f}.)
     *
     *    <li>Two corresponding {@code double} members whose values
     *    are {@code x} and {@code y} are considered equal if
     *    {@code Double.valueOf(x).equals(Double.valueOf(y))}.
     *    (Unlike the {@code ==} operator, NaN is considered equal
     *    to itself, and {@code 0.0} unequal to {@code -0.0}.)
     *
     *    <li>Two corresponding {@code String}, {@code Class}, enum, or
     *    annotation typed members whose values are {@code x} and {@code y}
     *    are considered equal if {@code x.equals(y)}.  (Note that this
     *    definition is recursive for annotation typed members.)
     *
     *    <li>Two corresponding array typed members {@code x} and {@code y}
     *    are considered equal if {@code Arrays.equals(x, y)}, for the
     *    appropriate overloading of {@link java.util.Arrays#equals}.
     * </ul>
     *
     * @return true if the specified object represents an annotation
     *     that is logically equivalent to this one, otherwise false
     */
    boolean equals(Object obj);

    /**
     * Returns the hash code of this annotation, as defined below:
     *
     * <p>The hash code of an annotation is the sum of the hash codes
     * of its members (including those with default values), as defined
     * below:
     *
     * The hash code of an annotation member is (127 times the hash code
     * of the member-name as computed by {@link String#hashCode()}) XOR
     * the hash code of the member-value, as defined below:
     *
     * <p>The hash code of a member-value depends on its type:
     * <ul>
     * <li>The hash code of a primitive value <i>{@code v}</i> is equal to
     *     <code><i>WrapperType</i>.valueOf(<i>v</i>).hashCode()</code>, where
     *     <i>{@code WrapperType}</i> is the wrapper type corresponding
     *     to the primitive type of <i>{@code v}</i> ({@link Byte},
     *     {@link Character}, {@link Double}, {@link Float}, {@link Integer},
     *     {@link Long}, {@link Short}, or {@link Boolean}).
     *
     * <li>The hash code of a string, enum, class, or annotation member-value
     I     <i>{@code v}</i> is computed as by calling
     *     <code><i>v</i>.hashCode()</code>.  (In the case of annotation
     *     member values, this is a recursive definition.)
     *
     * <li>The hash code of an array member-value is computed by calling
     *     the appropriate overloading of
     *     {@link java.util.Arrays#hashCode(long[]) Arrays.hashCode}
     *     on the value.  (There is one overloading for each primitive
     *     type, and one for object reference types.)
     * </ul>
     *
     * @return the hash code of this annotation
     */
    int hashCode();

    /**
     * Returns a string representation of this annotation.  The details
     * of the representation are implementation-dependent, but the following
     * may be regarded as typical:
     * <pre>
     *   &#064;com.acme.util.Name(first=Alfred, middle=E., last=Neuman)
     * </pre>
     *
     * @return a string representation of this annotation
     */
    String toString();

    /**
     * Returns the annotation type of this annotation.
     * @return the annotation type of this annotation
     */
    Class<? extends Annotation> annotationType();
}

getAnnotations

先看一段代码


@MyAnnotation()
public class TestAnnotation {

    public static void main(String[] args) {
        System.out.println(Arrays.toString(TestAnnotation.class.getAnnotations()));
    }

}

[@MyAnnotation(a=0, b=0, c=0, d=0, e=0.0, f=0.0f, g=false, h="", i=java.lang.Object.class)]

发现问题没?

  • 注解是抽象的
  • getAnnotations明显拿到的就是实现类他是具体的,怎么做到的?
  • 这个实现类是谁帮我们实现的,又是怎么实现的?

透个底


@MyAnnotation()
public class TestAnnotation {

    public static void main(String[] args) {
        System.out.println(TestAnnotation.class.getAnnotations()[0].getClass().getName());
    }

}
com.sun.proxy.$Proxy1

java 注解的底层是依靠jdk动态代理动态生成实现类

详细

首先我们能推断出jdk动态代理肯定是在调用getClass之前生成的, 盲猜getAnnotations生成.

public Annotation[]getAnnotations(){
        return AnnotationParser.toArray(annotationData().annotations);
        }

img_4.png

private AnnotationData createAnnotationData(int classRedefinedCount){
        Map<Class<?extends Annotation>,Annotation>declaredAnnotations=
        AnnotationParser.parseAnnotations(getRawAnnotations(),getConstantPool(),this);
        Class<?> superClass=getSuperclass();
        Map<Class<?extends Annotation>,Annotation>annotations=null;
        //......
        //parse...
        //......
        return new AnnotationData(annotations,declaredAnnotations,classRedefinedCount);
        }
public static Map<Class<?extends Annotation>,Annotation>parseAnnotations(
        byte[]rawAnnotations,
        ConstantPool constPool,
        Class<?> container){
        if(rawAnnotations==null)
        return Collections.emptyMap();

        try{
        //继续解析
        return parseAnnotations2(rawAnnotations,constPool,container,null);
        }catch(BufferUnderflowException e){
        throw new AnnotationFormatError("Unexpected end of annotations.");
        }catch(IllegalArgumentException e){
        // Type mismatch in constant pool
        throw new AnnotationFormatError(e);
        }
        }
private static Map<Class<?extends Annotation>,Annotation>parseAnnotations2(
        byte[]rawAnnotations,
        ConstantPool constPool,
        Class<?> container,
        Class<?extends Annotation>[]selectAnnotationClasses){
        Map<Class<?extends Annotation>,Annotation>result=
        new LinkedHashMap<Class<?extends Annotation>,Annotation>();
        ByteBuffer buf=ByteBuffer.wrap(rawAnnotations);
        int numAnnotations=buf.getShort()&0xFFFF;
        for(int i=0;i<numAnnotations; i++){
        //继续parse
        Annotation a=parseAnnotation2(buf,constPool,container,false,selectAnnotationClasses);
        if(a!=null){
        Class<?extends Annotation> klass=a.annotationType();
        if(AnnotationType.getInstance(klass).retention()==RetentionPolicy.RUNTIME&&
        result.put(klass,a)!=null){
        throw new AnnotationFormatError(
        "Duplicate annotation for class: "+klass+": "+a);
        }
        }
        }
        return result;
        }
 static Annotation parseAnnotation(ByteBuffer buf,
        ConstantPool constPool,
        Class<?> container,
        boolean exceptionOnMissingAnnotationClass){
        return parseAnnotation2(buf,constPool,container,exceptionOnMissingAnnotationClass,null);
        }

@SuppressWarnings("unchecked")
private static Annotation parseAnnotation2(ByteBuffer buf,
        ConstantPool constPool,
        Class<?> container,
        boolean exceptionOnMissingAnnotationClass,
        Class<?extends Annotation>[]selectAnnotationClasses){
        //......
        //海量的解析
        return annotationForMap(annotationClass,memberValues);
        }
public static Annotation annotationForMap(final Class<?extends Annotation> type,
final Map<String, Object> memberValues)
        {
        return AccessController.doPrivileged(new PrivilegedAction<Annotation>(){
public Annotation run(){
        //露出尾巴了
        return(Annotation)Proxy.newProxyInstance(
        type.getClassLoader(),new Class<?>[]{type},
        new AnnotationInvocationHandler(type,memberValues));
        }});
        }

后续的invoke就是由AnnotationInvocationHandler进行代理

至于生成的字节码嘛,开启属性即可

System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");

可以发现这个代理类代理的接口就是我们的注解

public final class $Proxy1 extends Proxy implements MyAnnotation {
    private static Method m1;
    private static Method m5;
    private static Method m9;
    private static Method m8;
    private static Method m6;
    private static Method m2;
    private static Method m7;
    private static Method m10;
    private static Method m12;
    private static Method m4;
    private static Method m11;
    private static Method m3;
    private static Method m0;

    public $Proxy1(InvocationHandler var1) throws {
        super(var1);
    }

    public final boolean equals(Object var1) throws {
        try {
            return (Boolean) super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final short b() throws {
        try {
            return (Short) super.h.invoke(this, m5, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final byte a() throws {
        try {
            return (Byte) super.h.invoke(this, m9, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final long d() throws {
        try {
            return (Long) super.h.invoke(this, m8, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int c() throws {
        try {
            return (Integer) super.h.invoke(this, m6, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws {
        try {
            return (String) super.h.invoke(this, m2, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final float f() throws {
        try {
            return (Float) super.h.invoke(this, m7, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final double e() throws {
        try {
            return (Double) super.h.invoke(this, m10, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final Class annotationType() throws {
        try {
            return (Class) super.h.invoke(this, m12, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String h() throws {
        try {
            return (String) super.h.invoke(this, m4, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean g() throws {
        try {
            return (Boolean) super.h.invoke(this, m11, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final Class i() throws {
        try {
            return (Class) super.h.invoke(this, m3, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws {
        try {
            return (Integer) super.h.invoke(this, m0, (Object[]) null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("MyAnnotation").getMethod("b");
            m9 = Class.forName("MyAnnotation").getMethod("a");
            m8 = Class.forName("MyAnnotation").getMethod("d");
            m6 = Class.forName("MyAnnotation").getMethod("c");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m7 = Class.forName("MyAnnotation").getMethod("f");
            m10 = Class.forName("MyAnnotation").getMethod("e");
            m12 = Class.forName("MyAnnotation").getMethod("annotationType");
            m4 = Class.forName("MyAnnotation").getMethod("h");
            m11 = Class.forName("MyAnnotation").getMethod("g");
            m3 = Class.forName("MyAnnotation").getMethod("i");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

小结

  • 注解在class字节码层面上就是一个extends Annotation的接口
  • 注解source,class时期是依靠的java compiler编译期实现
  • 注解的runtime数据的获取是依靠的jdk动态代理动态生成实现类