Java 注解

1. 什么是注解?

Java 注解又被称为标注,是 JDK 1.5 引入的一种注释机制。

Java 源码的类、方法、参数、变量等前的一种特殊“注释”。

Java 语言中类、方法、变量、参数和包等可以被标注。

注解就是用作标注的“元数据”。

和 Javadoc 不同,Java 标注分为运行时和编译时两个实现。

运行时:Java 标注在运行时可以通过反射获取标注内容。

编译时:Java 标注在编译生成类文件时,标注可以被嵌入到字节码中,Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容。

PS:注解不会影响代码正常逻辑执行。

2. 注解本质

java.lang.annotation.Annotation 接口中是这样描述注解:所有的注解类型都继承于 Annotation 接口。

3. 注解的好处

  1. 保存于 class 文件中,降低维护成本。
  2. 无需工具支持,无需解析。
  3. 编译期即可验证正确性,查错变得容易。
  4. 提升开发效率。

4. 注解实现

  1. 什么是元注解?

  元注解:用于修饰注解的注解,通常用在注解的定义上。

  • @target:作用于注解的目标,标记这个应用作用于哪些 Java 成员,包括:类、方法、参数、变量、包等。
  • Retention:注解的生命周期,标记这个注解什么时保存,1. 保存于代码中;2. 保存于编译生成 class 文件中;3. 保存于运行时通过反射访问。
  • Documented:注解是否被包含在 Javadoc 文档中。
  • @Inherited:是否允许子类继承该注解。

Java 中 @Override 注解实现:

 

/**
 * Indicates that a method declaration is intended to override a
 * method declaration in a supertype. If a method is annotated with
 * this annotation type compilers are required to generate an error
 * message unless at least one of the following conditions hold:
 *
 * <ul><li>
 * The method does override or implement a method declared in a
 * supertype.
 * </li><li>
 * The method has a signature that is override-equivalent to that of
 * any public method declared in {@linkplain Object}.
 * </li></ul>
 *
 * @author  Peter von der Ah&eacute;
 * @author  Joshua Bloch
 * @jls 9.6.1.4 @Override
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

编译后:

public interface Override extends Annotation{

}

注解本质就是一个继承了 Annotation 的接口。

注解其实就是一个特殊的注释,如果不解析它的代码,注解就没有任何作用。

  2. 元注解的实现

  @Target:

  源码:

/**
 * Indicates the contexts in which an annotation type is applicable. The
 * declaration contexts and type contexts in which an annotation type may be
 * applicable are specified in JLS 9.6.4.1, and denoted in source code by enum
 * constants of {@link ElementType java.lang.annotation.ElementType}.
 *
 * <p>If an {@code @Target} meta-annotation is not present on an annotation type
 * {@code T} , then an annotation of type {@code T} may be written as a
 * modifier for any declaration except a type parameter declaration.
 *
 * <p>If an {@code @Target} meta-annotation is present, the compiler will enforce
 * the usage restrictions indicated by {@code ElementType}
 * enum constants, in line with JLS 9.7.4.
 *
 * <p>For example, this {@code @Target} meta-annotation indicates that the
 * declared type is itself a meta-annotation type.  It can only be used on
 * annotation type declarations:
 * <pre>
 *    &#064;Target(ElementType.ANNOTATION_TYPE)
 *    public &#064;interface MetaAnnotationType {
 *        ...
 *    }
 * </pre>
 *
 * <p>This {@code @Target} meta-annotation indicates that the declared type is
 * intended solely for use as a member type in complex annotation type
 * declarations.  It cannot be used to annotate anything directly:
 * <pre>
 *    &#064;Target({})
 *    public &#064;interface MemberType {
 *        ...
 *    }
 * </pre>
 *
 * <p>It is a compile-time error for a single {@code ElementType} constant to
 * appear more than once in an {@code @Target} annotation.  For example, the
 * following {@code @Target} meta-annotation is illegal:
 * <pre>
 *    &#064;Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
 *    public &#064;interface Bogus {
 *        ...
 *    }
 * </pre>
 *
 * @since 1.5
 * @jls 9.6.4.1 @Target
 * @jls 9.7.4 Where Annotations May Appear
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

  元注解 Target 作用就是注解作用于谁,指明注解修改的是类、方法、变量或者参数等等。

  Target 的元素类型:

/**
 * The constants of this enumerated type provide a simple classification of the
 * syntactic locations where annotations may appear in a Java program. These
 * constants are used in {@link Target java.lang.annotation.Target}
 * meta-annotations to specify where it is legal to write annotations of a
 * given type.
 *
 * <p>The syntactic locations where annotations may appear are split into
 * <em>declaration contexts</em> , where annotations apply to declarations, and
 * <em>type contexts</em> , where annotations apply to types used in
 * declarations and expressions.
 *
 * <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
 * #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
 * {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
 * to the declaration contexts in JLS 9.6.4.1.
 *
 * <p>For example, an annotation whose type is meta-annotated with
 * {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
 * field declaration.
 *
 * <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS
 * 4.11, as well as to two declaration contexts: type declarations (including
 * annotation type declarations) and type parameter declarations.
 *
 * <p>For example, an annotation whose type is meta-annotated with
 * {@code @Target(ElementType.TYPE_USE)} may be written on the type of a field
 * (or within the type of the field, if it is a nested, parameterized, or array
 * type), and may also appear as a modifier for, say, a class declaration.
 *
 * <p>The {@code TYPE_USE} constant includes type declarations and type
 * parameter declarations as a convenience for designers of type checkers which
 * give semantics to annotation types. For example, if the annotation type
 * {@code NonNull} is meta-annotated with
 * {@code @Target(ElementType.TYPE_USE)}, then {@code @NonNull}
 * {@code class C {...}} could be treated by a type checker as indicating that
 * all variables of class {@code C} are non-null, while still allowing
 * variables of other classes to be non-null or not non-null based on whether
 * {@code @NonNull} appears at the variable's declaration.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.4.1 @Target
 * @jls 4.1 The Kinds of Types and Values
 */
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Field declaration (includes enum constants) </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
FIELD,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Method declaration </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
METHOD,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Formal parameter declaration </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
PARAMETER,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Constructor declaration </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
CONSTRUCTOR,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Local variable declaration </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
LOCAL_VARIABLE,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Annotation type declaration </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
ANNOTATION_TYPE,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Package declaration </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
PACKAGE,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Type parameter declaration
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@since</span><span style="color: rgba(0, 128, 0, 1)"> 1.8
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
TYPE_PARAMETER,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Use of a type
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@since</span><span style="color: rgba(0, 128, 0, 1)"> 1.8
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
TYPE_USE

}

    • ElementType.Type:允许被修饰的注解作用于类、接口、枚举上。
    • ElementType.FIELD:允许作用于属性上。
    • ElementType.METHOD:允许作用于方法上。
    • ElementType.PARAMETER:允许作用于参数上。
    • ElementType.CONSTRUCTOR:允许作用于构造函数上。
    • ElementType.LOCAL_VARIABLE:允许作用于本地局部变量上。
    • ElementType.ANNOTATION_TYPE:允许作用于注解上。
    • ElementType.PACKAGE:允许作用于包上。

  示例:

@Target(ElementType.METHOD)

 

  @Retention:作用指明注解的生命周期。

/**
 * Annotation 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.
     */
    SOURCE,
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 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.
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
CLASS,

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 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.
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@see</span><span style="color: rgba(0, 128, 0, 1)"> java.lang.reflect.AnnotatedElement
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
RUNTIME

}

  RetentionPolicy.SOURCE:当前注解编译期可见,但不会写入 class 文件。

  RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件。

  RetentionPolicy.RUNTIME:永久保存,运行时可以通过反射获取。

  示例:

@Retention(RetentionPolicy.SOURCE)

 5. Java 内置三大注解

  • @Override
  • @Deprecated
  • @SuppressWarnings

6. 注解处理机制及原理

   1. 运行时通过反射获取注解内容元数据

    @URIParse

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD, ElementType.LOCAL_VARIABLE})
public @interface URIParse {
String value()
default "";
}

  通过反射获取注解内容:

import java.lang.reflect.Field;

public class AnnotationParse {

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> annotation(Object target) {
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {

        Class</span>&lt;?&gt; forName =<span style="color: rgba(0, 0, 0, 1)"> Class.forName(target.getClass().getName());
        </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Field field : forName.getDeclaredFields()) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (field.isAnnotationPresent(URIParse.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)) {
                field.setAccessible(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);</span><span style="color: rgba(0, 0, 0, 1)">
                field.set(target, field.getAnnotation(URIParse.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">).value());
            }
        }
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span> (ClassNotFoundException |<span style="color: rgba(0, 0, 0, 1)"> IllegalAccessException e) {
        e.printStackTrace();
    }

}

}

  2. 编译时注解处理器(Annotation Processor:注解处理器)

  什么是注解处理器?

  注解处理器是 javac 的一个工具,用于在编译时扫描和处理注解(Annotation)。 

  编译时注解处理器处理流程:

 

  @RDateFormat

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(value = RetentionPolicy.CLASS)
@Target(value
= {ElementType.FIELD, ElementType.LOCAL_VARIABLE})
public @interface RDateFormat {
String value()
default "";
String format()
default "";
}

  注解处理器:

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.LinkedHashSet;
import java.util.Set;

public class RDateFormatProcessor extends AbstractProcessor {

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * If the processor class is annotated with {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)">
 * SupportedOptions}, return an unmodifiable set with the same set
 * of strings as the annotation.  If the class is not so
 * annotated, an empty set is returned.
 *
 * 自定义参数给注解处理器。
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the options recognized by this processor, or an empty
 * set if none
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> Set&lt;String&gt;<span style="color: rgba(0, 0, 0, 1)"> getSupportedOptions() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.getSupportedOptions();
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Initializes the processor with the processing environment by
 * setting the {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)"> processingEnv} field to the value of the
 * {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)"> processingEnv} argument.  An {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)">
 * IllegalStateException} will be thrown if this method is called
 * more than once on the same object.
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> processingEnv environment to access facilities the tool framework
 *                      provides to the processor
 * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> IllegalStateException if this method is called more than once.
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">synchronized</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> init(ProcessingEnvironment processingEnv) {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.init(processingEnv);
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 这相当于每个处理器的主函数main(),你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。
 * 输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> annotations 请求处理的注解类型
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> roundEnv    有关当前和以前的信息环境
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> 如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们;
 * 如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> process(Set&lt;? <span style="color: rgba(0, 0, 255, 1)">extends</span> TypeElement&gt;<span style="color: rgba(0, 0, 0, 1)"> annotations, RoundEnvironment roundEnv) {
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Element element : roundEnv.getElementsAnnotatedWith(RDateFormat.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)) {
        System.out.println(element.getEnclosingElement().getSimpleName());
        System.out.println(element.getEnclosingElement().getEnclosingElement().getSimpleName());
        System.out.println(element.getEnclosingElement().getEnclosingElement().getEnclosingElement().getSimpleName());
        System.out.println(element.getEnclosingElement().getEnclosingElement().getEnclosingElement().getEnclosingElement());
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * If the processor class is annotated with {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)">
 * SupportedAnnotationTypes}, return an unmodifiable set with the
 * same set of strings as the annotation.  If the class is not so
 * annotated, an empty set is returned.
 *
 * 这个方法必须覆写,用于指定注解是属于哪个注解处理器的,用于注解注册,返回一个注册的注解集合。
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the names of the annotation types supported by this
 * processor, or an empty set if none
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> Set&lt;String&gt;<span style="color: rgba(0, 0, 0, 1)"> getSupportedAnnotationTypes() {
    Set</span>&lt;String&gt; annotations = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    annotations.add(RDateFormat.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getCanonicalName());
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> annotations;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * If the processor class is annotated with {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)">
 * SupportedSourceVersion}, return the source version in the
 * annotation.  If the class is not so annotated, {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)">
 * SourceVersion#RELEASE_6} is returned.
 *
 * 返回JDK版本
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the latest source version supported by this processor
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> SourceVersion getSupportedSourceVersion() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">  SourceVersion.latestSupported();
}

}

  示例使用的是 Java 自带注解处理器,Google 提供的注解处理器:com.google.auto.service:auto-service:1.0-rc3