重新認識Java註解,帶你找回初戀的感覺


在開發過程中,註解是我們最爲常見的代碼之一,JDK內置註解、元註解、自定義註解等,例如Spring中的@Component、@Service、@Controller等;MybaitsPlus中@TableName等;JDK自帶註解@Override、@Deprecated、@SuppressWarnings等,

常見的情況下,註解可以作用在類、方法、成員變量等上。他是如何實現配置的?

此篇文章帶你從以下角度重新認識註解

  • 什麼是註解
  • 什麼是元註解
  • 元註解包含哪些
  • 自定義註解

1、什麼是註解

官方說法:

Annotation(註解)是JDK1.5及以後版本引入的。它可以用於創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。從某些方面看,annotation就像修飾符一樣被使用,並應用於包、類 型、構造方法、方法、成員變量、參數、本地變量的聲明中。這些信息被存儲在Annotation的“name=value”結構對中。

大白話就是註解其實是一個接口,是描述數據的數據,常與反射結伴而行。

2、爲什麼使用註解

那爲什麼要使用註解,因爲現在我們已經默認接受了註解,無法想象沒有註解的時代是什麼樣。

在JDK1.5之前,沒有推出註解的年代,怎麼實現描述數據?答案是使用XML。

舉個非常簡單的例子,Spring相信大家都是用過,Spring常用的編程風格有以下三種

  1. schemal-based-------xml
  2. annotation-based-----annotation
  3. java-based----java Configuration

如果你使用你經常使用annotation和-java Configuration,讓你在重新使用XML配置Spring你肯定無法接受。

3、註解的作用

  • 提供信息給編譯器: 編譯器可以利用註解來探測錯誤和警告信息,如 @Override、@Deprecated。
  • 編譯階段時的處理: 軟件工具可以用來利用註解信息來生成代碼、Html 文檔或者做其它相應處理,如 @Param、@Return、@See、@Author 用於生成 Javadoc 文檔。
  • 運行時的處理: 某些註解可以在程序運行的時候接受代碼的提取,值得注意的是,註解不是代碼本身的一部分。如Spring 2.5 開始註解配置,減少了配置。

4、註解的分類

根據註解的參數個數分類:

  • 標記註解,一個沒有成員的Annotation類型被稱爲標記註解,這種類型僅僅使用自身的存在與否來爲我們提供信息,比如常見的@Override
  • 單值註解
  • 完整註解

根據註解使用的方法和用途分類:

  • JDK內置系統註解
  • 元註解
  • 自定義註解

5、元註解

元註解的作用就是負責註解其他註解,java 5.0定義了4個meta-annotation類型。

  • @Target
  • @Retention
  • @Document
  • @Inhrited

5.1 @Target

修飾的對象範圍:packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。

作用:用於描述註解的使用範圍。

取值範圍(查看源碼,一目瞭然):

/*
 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang.annotation;

/**
 * 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 用於描述類、接口(包括註解類型) 或enum聲明*/
    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
}

【注】JDK1.8後新增了TYPE_PARAMETERTYPE_USE兩個枚舉值,這種註解被稱爲Type Annotation(類型註解),Type Annotation可用在任何用到類型的地方。

5.2 Retention

定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因爲Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命週期”限制。

作用:表示需要在什麼級別保存該註釋信息,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)

取值範圍(查看源碼,一目瞭然):

/*
 * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang.annotation;

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

    /**
     * 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保留)
     */
    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
}

【注】Name註解的RetentionPolicy的值爲RUNTIME,這樣註解處理器可以通過反射,獲取到該註解的屬性,從而做一些運行時的邏輯處理。

5.3 @Document

用於描述其它類型的annotation應該被作爲被標註的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。

作用:說明該註解將被包含在javadoc中

5.4 @Inhrited

是一個標記註解,@Inherited闡述了某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。

@Inherited annotation類型是被標註過的class的子類所繼承。類並不從它所實現的接口繼承annotation,方法並不從它所重載的方法繼承annotation

當@Inherited annotation類型標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達類繼承結構的頂層。

6、自定義註解

使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義註解時,不能繼承其他的註解或接口。@interface用來聲明一個註解,其中的每一個方法實際上是聲明瞭一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。

自定義註解格式:

public @interface 註解名{註解體}
1.所有基本數據類型(int,float,boolean,byte,double,char,long,short)
2.String類型
3.Class類型
4.enum類型
5.Annotation類型
6.以上所有類型的數組

Annotation類型裏面的參數該怎麼設定:

  1. 只能用public或默認(default)這兩個訪問權修飾.例如,String value();這裏把方法設爲defaul默認類型;
  2. 參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這裏的參數成員就爲String;
  3. 如果只有一個參數成員,最好把參數名稱設爲"value",後加小括號.例:下面的例子Name註解就只有一個參數成員。

eg:

/**
 * 自定義註解
 *
 * @author lixiang
 * @version V1.0
 * @date 2020/6/13 14:37
 **/
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Entity {

    String value() default "";
}

註解元素的默認值

註解元素必須有確定的值,要麼在定義註解的默認值中指定,要麼在使用註解時指定,非基本類型的註解元素的值不可爲null。因此, 使用空字符串或0作爲默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因爲每個註解的聲明中,所有元素都存在,並且都具有相應的值,爲了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義註解時,這已經成爲一個習慣用法。

與反射結合使用

/**通過反射獲取對象註解上的數據**/
Class<?> clazz = obj.getClass();
if (clazz.isAnnotationPresent(Entity.class)) {
    Entity declaredAnnotation = clazz.getDeclaredAnnotation(Entity.class);
    String value = declaredAnnotation.value();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章