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();
...判斷