美文网首页
元注解与注解是如何被加载的?如何生效的?

元注解与注解是如何被加载的?如何生效的?

作者: dylan丶QAQ | 来源:发表于2021-03-21 14:32 被阅读0次

元注解,是java定义的基本注解,我们可以在很多框架中看到他们的身影,今天我们就刨根问底的看看什么是元注解。一起干饭!


本章主要内容

  • 什么是注解,元注解?
  • 元注解的用途
  • 如何处理注解?

1.什么是注解,元注解?

Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据[1]

Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java虚拟机可以保留标注内容,在运行时可以获取到标注内容[2]。 当然它也支持自定义Java标注[3]

Java元注解是可以作用在其他注解的注解:
Java 5 时有4个分别为@Retention@Documented @Target @Inherited
从 Java 7 开始,额外添加了 3 个注解:@SafeVarargs @FunctionalInterface @Repeatable

2.元注解的用途?

Java 5 提供的4个元注解

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
  • @Documented - 标记这些注解是否包含在用户文档中。
  • @Target - 标记这个注解应该是哪种 Java 成员。
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

接下来以@RequestMapping()注解为例来解析元注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
}
2.1 @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 在运行时获取到,可以通过反射的方式解析到

注解是否会被写入字节码文件有什么区别?

2.2 @Documented注解

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

package java.lang.annotation;

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

什么是javadoc,为什么要被收入到javadoc?

2.3 @Target注解

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

package java.lang.annotation;

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的定义上

2.4 @Inherited注解

标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

package java.lang.annotation;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

使用此注解声明出来的自定义注解,在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解,否则的话,子类不会继承此注解。这里一定要记住,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。

3.java是如何处理注解的?

  1. 在类或方法上使用注解
  2. java编译器编译为.class文件
  3. 通过反射把.class文件加载到jvm中,生成唯一的Class对象。
    • JVM通过反射的方式读取注解信息
    • java反射包为注解提供了一个接口java.lang.reflect.AnnotatedElement
    • Class类实现了AnnotatedElement接口,在类加载时,会加载Annotation
    • 注解都默认继承自java.lang.annotation包下的Annotation接口

JVM如何将.class文件加载到jvm中?

反射是如何获取类的信息?

任何注解类型都默认继承自java.lang.annotation包下的Annotation接口,表明这是一个注解类型,这是编译器自动帮我们完成的。但是手动继承Annotation没有这个效果,即不会把它当成注解类型。甚至Annotation接口本身也并不意味着它是注解类型。可以简单的理解为:我们可以也只可以通过@interface的方式来定义注解类型,这个注解类型默认会实现Annotation接口。

注解通过设置可以一直保留到运行期,此时VM通过反射的方式读取注解信息。由上面的介绍可知,注解是解释代码的代码,它必须存在于特定的代码元素之上,可以是类,可以是方法,可以是字段等等。
为了更好的在运行时解析这些代码元素上的注解,java在反射包下为它们提供了一个接口java.lang.reflect.AnnotatedElement


不要以为每天把功能完成了就行了,这种思想是要不得的,互勉~!若文章对您有用,请点赞支持哦。

相关文章

网友评论

      本文标题:元注解与注解是如何被加载的?如何生效的?

      本文链接:https://www.haomeiwen.com/subject/ithbcltx.html