自定義註解
1.先看一個dome
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAutoRecord {
// 強制記錄方法功能(使用註解必須填寫該屬性)
String methodDesc();
// 帶有預設值,非必填
String defValue() default "";
}
對於上述dome中,有以下幾個屬性。
-
註解定義用
@interface
關鍵字修飾 -
@Target
註解,是專門用來限定某個自定義註解能夠被應用在哪些Java元素上面的 -
@Retention
註解,翻譯為持久力、保持力。即用來修飾自定義註解的生命週期 -
@Documented
註解,是被用來指定自定義註解是否能隨著被定義的java檔案生成到JavaDoc文件當中 -
@Inherited
註解,是指定某個自定義註解如果寫在了父類的宣告部分,那麼子類的宣告部分也能自動擁有該註解
2.屬性解釋
-
用
@interface
關鍵字修飾,表明這是一個註解,可將註解掛到別的頭上,至於掛到哪要看@Target註解了。 -
@Target
註解,是專門用來限定某個自定義註解能夠被應用在哪些Java元素上面的@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[] ,是一個陣列,可以傳多個,如:
@Target({ElementType.METHOD,ElementType.TYPE})
上面是修飾的註解表明該註解可以用在方法上,也可以用在類上。具體屬性看原始碼
/** 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 * 型別引數宣告,1.8以後才有 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 字型的使用 */ TYPE_USE
-
@Retention
註解,可以理解為生效時期Retention 原始碼
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
Retention 原始碼中屬性值為RetentionPolicy物件,所以不能像Target一樣配置多個,下面看一下RetentionPolicy屬性的原始碼:
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. * 編譯器將丟棄註釋。 * 這個屬性的表明只會將註解資訊儲存到程式原始碼中,在經過編譯器編譯後就會將資訊拋棄,不會儲存到.class檔案中,也不會被載入到Jvm中。基本就是看著玩呢,標記一下 */ 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檔案中,但不會載入到jvm中。標記一下,讀取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 * 這個屬性的表明只會將註解資訊儲存到程式原始碼中,也會儲存到.class檔案中,也會載入到jvm中。在程式中可以讀取到哪些類或方法標記了該註解,便於我們後續操作 */ RUNTIME }
測試這三個屬性,建立三個註解,Retention分別標記為RUNTIME,SOURCE,CLASS
@Retention(RetentionPolicy.RUNTIME) public @interface LogAutoRecord { // 省略 1 @Retention(RetentionPolicy.CLASS) public @interface LogAutoRecordTestClass { // 省略2 @Retention(RetentionPolicy.SOURCE) public @interface LogAutoRecordTestSource { // 省略 3
controller
// 我在controller中使用註解,方法如下: @GetMapping("/getUserByCommon") @LogAutoRecord(methodDesc = "根據統一請求查詢使用者資訊") public CommonResponse getUserByCommonRequest(@RequestBody UserRequestDTO userRequestDTO){ String userById = userService.getUserById(userRequestDTO.getUserId()); return CommonResponse.succeed(userById); } @GetMapping("/getUserByCommonTestSource") @LogAutoRecordTestSource(methodDesc = "根據統一請求查詢使用者資訊,測試註解屬性source") public CommonResponse getUserByCommonTestSource(@RequestBody UserRequestDTO userRequestDTO){ String userById = userService.getUserById(userRequestDTO.getUserId()); return CommonResponse.succeed(userById); } @GetMapping("/getUserByCommonTestClass") @LogAutoRecordTestClass(methodDesc = "根據統一請求查詢使用者資訊,測試註解屬性Class") public CommonResponse getUserByCommonTestClass(@RequestBody UserRequestDTO userRequestDTO){ String userById = userService.getUserById(userRequestDTO.getUserId()); return CommonResponse.succeed(userById); }
編譯後的class檔案
// 編譯後的.class 檔案 @GetMapping({"/getUserByCommon"}) @LogAutoRecord( methodDesc = "根據統一請求查詢使用者資訊" ) public CommonResponse getUserByCommonRequest(@RequestBody UserRequestDTO userRequestDTO) { String userById = this.userService.getUserById(userRequestDTO.getUserId()); return CommonResponse.succeed(userById); } @GetMapping({"/getUserByCommonTestSource"}) public CommonResponse getUserByCommonTestSource(@RequestBody UserRequestDTO userRequestDTO) { String userById = this.userService.getUserById(userRequestDTO.getUserId()); return CommonResponse.succeed(userById); } @GetMapping({"/getUserByCommonTestClass"}) @LogAutoRecordTestClass( methodDesc = "根據統一請求查詢使用者資訊,測試註解屬性Class" ) public CommonResponse getUserByCommonTestClass(@RequestBody UserRequestDTO userRequestDTO) { String userById = this.userService.getUserById(userRequestDTO.getUserId()); return CommonResponse.succeed(userById); }
根據編譯後的檔案可以看出source標記的編譯後就不見了,class的還在。
-
@Documented
註解,是被用來指定自定義註解是否能隨著被定義的java檔案生成到JavaDoc文件當中 -
@Inherited
註解,是指定某個自定義註解如果寫在了父類的宣告部分,那麼子類的宣告部分也能自動擁有該註解@Inherited註解只對那些@Target被定義為ElementType.TYPE的自定義註解起作用。
3. 獲取註解
-
給出指定包
public class GetAnnotation { public static void getAnnotation() { // 要掃描的包 String packageName = "com.zhoust.fastdome.business.controller"; Reflections f = new Reflections(packageName); // 獲取掃描到的標記註解的集合 Set<Class<?>> set = f.getTypesAnnotatedWith(LogAutoRecord.class); for (Class<?> c : set) { // 迴圈獲取標記的註解 LogAutoRecord annotation = c.getAnnotation(LogAutoRecord.class); // 列印註解中的內容 System.out.println(annotation.methodDesc()); } } }
使用Reflections反射需要匯入反射包,或者自己使用java自帶的反射包
<dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency>
-
aop切面 中獲取註解的物件
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String methodName = method.getName(); LogAutoRecord annotation = method.getAnnotation(LogAutoRecord.class); String methodDesc = "記錄日誌"; if(null != annotation){ methodDesc = annotation.methodDesc(); }
活到老學到老