Java元注解和注解的解析

在使用注解之前,需要了解:一、什么是注解?二、注解是如何产生作用的?而元注解是一个很好的切入点

元注解的源码结构

 

从java.lang.annotation包截图看,一共定义了6个注解:

  • @Document
  • @Target
  • @Retention
  • @Inherited
  • @Native
  • @Repeatable

其中前4个是元注解

元注解的定义

元注解负责注解自定义注解,你可以看到许多自定义的注解上面都有这些元注解:
例如spring mvc的注解@RequestMapping

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping{
    ......
}

元注解的用途

@Target
标识注解的使用范围,可以赋值为ElementType类型,ElementType定义如下:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

仍然以@RequestMapping为例,它的@Target赋值为{ElementType.METHOD, ElementType.TYPE},参考Element注释可以理解@RequestMapping可以用于java的方法定义及Class/Interfac/enum的定义上

@Retention
可以赋值 RetentionPolicy类型,RetentionPolicy定义如下:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    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,

    /**
     * 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
}

RetentionPolicy.SOURCE:表明注解会被编译器丢弃,字节码中不会带有注解信息
RetentionPolicy.CLASS:表明注解会被写入字节码文件,且是@Retention的默认值
RetentionPolicy.RUNTIME:表明注解会被写入字节码文件,并且能够被JVM 在运行时获取到,可以通过反射的方式解析到

@Documented

/**
 * Indicates that annotations with a type are to be documented by javadoc
 * and similar tools by default.  This type should be used to annotate the
 * declarations of types whose annotations affect the use of annotated
 * elements by their clients.  If a type declaration is annotated with
 * Documented, its annotations become part of the public API
 * of the annotated elements.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

从注释可以看到 @Document注解用途主要是标识类型是否要被收入javadoc

如何处理注解?

jdk中是通过AnnotatedElement(package java.lang.reflect)接口实现对注解的解析,我们的Class类实现了AnnotatedElement接口

public final class Class implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
  ......
}

AnnotatedElement代码:

 

AnnotatedElement的注释:
Represents an annotated element of the program currently running in this VM. This interface allows annotations to be read reflectively
翻译过来就是:AnnotatedElement代表了jvm中一个正在运行的被注解元素,这个接口允许通过反射的方式读取注解
可以看下Class类中对于AnnotatedElement接口都是如何实现的:

    /**
     * @throws NullPointerException {@inheritDoc}
     * @since 1.5
     */
    @SuppressWarnings("unchecked")
    public  A getAnnotation(Class annotationClass) {
        Objects.requireNonNull(annotationClass);

        return (A) annotationData().annotations.get(annotationClass);
    }

    /**
     * {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     * @since 1.5
     */
    @Override
    public boolean isAnnotationPresent(Class annotationClass) {
        return GenericDeclaration.super.isAnnotationPresent(annotationClass);
    }

    /**
     * @throws NullPointerException {@inheritDoc}
     * @since 1.8
     */
    @Override
    public  A[] getAnnotationsByType(Class annotationClass) {
        Objects.requireNonNull(annotationClass);

        AnnotationData annotationData = annotationData();
        return AnnotationSupport.getAssociatedAnnotations(annotationData.declaredAnnotations,
                                                          this,
                                                          annotationClass);
    }

    /**
     * @since 1.5
     */
    public Annotation[] getAnnotations() {
        return AnnotationParser.toArray(annotationData().annotations);
    }

    /**
     * @throws NullPointerException {@inheritDoc}
     * @since 1.8
     */
    @Override
    @SuppressWarnings("unchecked")
    public  A getDeclaredAnnotation(Class annotationClass) {
        Objects.requireNonNull(annotationClass);

        return (A) annotationData().declaredAnnotations.get(annotationClass);
    }

    /**
     * @throws NullPointerException {@inheritDoc}
     * @since 1.8
     */
    @Override
    public  A[] getDeclaredAnnotationsByType(Class annotationClass) {
        Objects.requireNonNull(annotationClass);

        return AnnotationSupport.getDirectlyAndIndirectlyPresent(annotationData().declaredAnnotations,
                                                                 annotationClass);
    }

    /**
     * @since 1.5
     */
    public Annotation[] getDeclaredAnnotations()  {
        return AnnotationParser.toArray(annotationData().declaredAnnotations);
    }

上面的接口实现中,大致的原理都是一致的,我们挑选其中的getAnnotation方法来讲解:

  • getAnnotation
    根据注解的class实例从类的注解缓存数据中获取匹配的注解类型
    Controller是注解类型,Controller.getClass()获取到的就是Class实例
    1、代码中annotationData().annotations是一个Map(key为注解的Class实例,value为注解类型),源码为:
    // annotation data that might get invalidated when JVM TI RedefineClasses() is called
    private static class AnnotationData {
        final Map, Annotation> annotations;
        final Map, Annotation> declaredAnnotations;

        // Value of classRedefinedCount when we created this AnnotationData instance
        final int redefinedCount;

        AnnotationData(Map, Annotation> annotations,
                       Map, Annotation> declaredAnnotations,
                       int redefinedCount) {
            this.annotations = annotations;
            this.declaredAnnotations = declaredAnnotations;
            this.redefinedCount = redefinedCount;
        }
    }

2、annotationData()的源码是:

    private AnnotationData annotationData() {
        while (true) { // retry loop
            AnnotationData annotationData = this.annotationData;
            int classRedefinedCount = this.classRedefinedCount;
            if (annotationData != null &&
                annotationData.redefinedCount == classRedefinedCount) {
                return annotationData;
            }
            // null or stale annotationData -> optimistically create new instance
            AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount);
            // try to install it
            if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) {
                // successfully installed new AnnotationData
                return newAnnotationData;
            }
        }
    }

核心的逻辑是:当this.annotationData为空,解析类中的annotationData并写入this.annotationData,最后都会返回this.annotationData

3、其中Atomic.casAnnotationData(this, annotationData, newAnnotationData)的作用便是将解析到的annotationData写入this.annotationData

        static  boolean casAnnotationData(Class clazz,
                                             AnnotationData oldData,
                                             AnnotationData newData) {
            return unsafe.compareAndSwapObject(clazz, annotationDataOffset, oldData, newData);
        }

其中unsafe.compareAndSwapObject是一个native方法
4、而createAnnotationData(classRedefinedCount)的作用是解析类中用到的annotationData

    private AnnotationData createAnnotationData(int classRedefinedCount) {
        Map, Annotation> declaredAnnotations =
            AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);
        Class superClass = getSuperclass();
        Map, Annotation> annotations = null;
        if (superClass != null) {
            Map, Annotation> superAnnotations =
                superClass.annotationData().annotations;
            for (Map.Entry, Annotation> e : superAnnotations.entrySet()) {
                Class annotationClass = e.getKey();
                if (AnnotationType.getInstance(annotationClass).isInherited()) {
                    if (annotations == null) { // lazy construction
                        annotations = new LinkedHashMap<>((Math.max(
                                declaredAnnotations.size(),
                                Math.min(12, declaredAnnotations.size() + superAnnotations.size())
                            ) * 4 + 2) / 3
                        );
                    }
                    annotations.put(annotationClass, e.getValue());
                }
            }
        }
        if (annotations == null) {
            // no inherited annotations -> share the Map with declaredAnnotations
            annotations = declaredAnnotations;
        } else {
            // at least one inherited annotation -> declared may override inherited
            annotations.putAll(declaredAnnotations);
        }
        return new AnnotationData(annotations, declaredAnnotations, classRedefinedCount);
    }

整个的处理逻辑是:
1、获取类本身的declaredAnnotations
2、获取父类的annotations
3、将declaredAnnotations+annotations整合,返回

Annotation解析的范例代码:

@Component
public class SSHClient {
  ......
}
public class AnnotationHelper {

    public static void main(String[] args) {
        Annotation[] annotations = new Annotation[0];

        annotations = SSHClient.class.getAnnotations();

        for (Annotation annotation : annotations) {
            System.out.println(annotation.toString());
            System.out.println(annotation.annotationType());
            System.out.println(annotation.getClass().getName());
            System.out.println(annotation.getClass().getTypeName());
            System.out.println(annotation.getClass().toString());
        }

        if (SSHClient.class.isAnnotationPresent(Component.class)) {
            System.out.println("find Component annotation");
        }

        Annotation annotation = SSHClient.class.getAnnotation(Component.class);

        System.out.println(annotation.toString());
        System.out.println(annotation.annotationType());
        System.out.println(annotation.getClass().getName());
        System.out.println(annotation.getClass().getTypeName());
        System.out.println(annotation.getClass().toString());
    }
}

执行结果:

 

可以看到,通过Annotation接口中定义的annotationType()可以获取Annotation的类型

实际应用中,比如spring框架中对注解的解析有专门的工具类,但是都是基于AnnotatedElement中定义的方法来实现的

以上,就是整个元注解和注解解析相关的讲解。



作者:测试你个头
链接:https://www.jianshu.com/p/e9329c8a59c2
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

你可能感兴趣的:(Java元注解和注解的解析)