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

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

作者: 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