Java自定義注解的實現


Java自定義注解的實現

簡介

注解:說明程序的,給計算機看的。

注釋:用文字描述程序的,給程序員看的。

定義:注解(Annotation),也叫元數據,一種代碼級別的說明,它是JDK1.5以后版本引入的一個特性,與類、接口、枚舉在同一個層次。可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。

概念描述:

  • JDK1.5之后的新特性
  • 用於說明程序
  • 使用注解:@注解名稱

作用分類:

  • 編寫文檔:通過代碼里標識的注解生成文檔
  • 代碼分析:通過注解對代碼進行分析(利用反射)
  • 編譯檢查:通過代碼里的注解讓編譯器能夠實現基本的編譯檢查例如Override

JDK中預定義的一些注解

  • @Override:檢測被該注解標注的方法是否是繼承自夫類/接口的
  • @Deprecated:表示該注解標注的內容,已過時
  • @SuppressWarnings:壓制警告

自定義注解

注解的定義規則

觀察預定義注解的源碼

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

可以看出,注解的定義方法是

@元注解
@元注解
....
public @interface 注解名{

}

其中,元注解可以省略,元注解的概念,我們下面再說。

注解的本質

模仿上面定義的方式,創建一個自定義注解

public @interface MyAnno{

}

使用javac命令編譯,查看源碼:

public interface MyAnno extends java.lang.annotation.Annotation{}

所以注解本質上就是一個接口,其默認繼承了Annotation接口

注解中的屬性

注解本質就是一個接口,所以屬性本質上就是接口中可以定義的成員方法。

接口里面能定義的,注解內都可以定義

屬性返回值的要求:

  • 基本數據類型
  • String
  • 枚舉
  • 注解
  • 以上的數據類型
  • 不能是void或者包裝類型

下面是定義一個包含屬性的注解的聲明:

package cn.rayfoo.common.annotation.validate;

/**
 * @author rayfoo@qq.com
 * @version 1.0
 * <p></p>
 * @date 2020/8/7 11:19
 */
public @interface NotNull {

    int myProp1();

    String myProp2();

    long[] myProp3();

}

此時,在使用注解的時候,必須給屬性賦值

屬性的修飾default

使用注解時,不需要給注解賦值,可以在屬性后加default修飾。不加修飾的為必須指定的內容。

package cn.rayfoo.common.annotation.validate;

/**
 * @author rayfoo@qq.com
 * @version 1.0
 * <p></p>
 * @date 2020/8/7 11:19
 */
public @interface NotNull {

    int myProp1() default 10;

    String myProp2() default "rayfoo";

    long[] value();

}

使用帶有參數的注解

數字屬性使用{}修飾

@NotNull(prop2 = "giao",value={1L,2L})

如果有且只有一個必須賦值的屬性,並且屬性名為value,屬性的name可以省略

@NotNull(value={1L,2L})

數組中如果只有一個元素,大括號可以省略

@NotNull(value=1L})

帶有注解類型屬性的使用方式

    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "用戶名", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "password", value = "密碼", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "phoneNumber", value = "手機號", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "signature", value = "簽名", required = false, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "email", value = "郵箱", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "code", value = "驗證碼", required = true, dataType = "String", paramType = "query"),
    })

包含枚舉屬性的注解使用方式

@RequestMapping(value = "/user",method = HttpMethod.DELETE)

元注解

元注解就是用於描述注解的注解

@Target:描述注解能夠作用的位置

下面時target的源碼,其只有一個枚舉類數組類型的屬性ElementType[]

@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();
}

ElementType取值

public enum ElementType {
    /** 類和接口上 */
    TYPE,

    /** 字段上 */
    FIELD,

    /** 方法上 */
    METHOD,

    /** 參數上 */
    PARAMETER,

    /** 構造上 */
    CONSTRUCTOR,

    /** 本地變量上 */
    LOCAL_VARIABLE,

    /** 注解上 */
    ANNOTATION_TYPE,

    /** 包上 */
    PACKAGE,

    /**
     * 類型參數上
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

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

這個可以根據自己的需要,添加合適的修飾。

@Retention:描述注解能夠保留的階段

觀察源碼,可以看出,其只有一個枚舉類型的屬性

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

RetentionPolicy取值范圍

public enum RetentionPolicy {
    /**
     * 注解只保留在源文件,當Java文件編譯成class文件的時候,注解被遺棄;
     */
    SOURCE,

    /**
     * 注解被保留到class文件,但jvm加載class文件時候被遺棄,這是默認的生命周期;
     */
    CLASS,

    /**
     * 注解不僅被保存到class文件中,jvm加載class文件之后,仍然存在;(自定義注解一般都用RUNTIME)
     */
    RUNTIME
}

@Retention:描述注解能夠保留的階段

@Documented:描述注解是否能被抽取到API文檔中

加上該注解后,其修飾的注解會被抽取到JavaDoc文檔中

@Inherited:描述注解是否可以被子類繼承

加入此注解后繼承了加了此注解修飾的類的子類,也會自動加上該注解修飾的注解

注解的掃描

加入注解后,對類沒有任何的影響,真正進行操作的是讀取注解處,我們可以借助反射中的知識,讀取並借助注解的屬性進行一些操作。一般配合AOP一起使用。

field.isAnnotationPresent(注解.class)
注解 verify = field.getAnnotation(注解.class);
String name = 注解.name();
...判斷


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM