概述
在 Java 開發中,自定義註解(Annotation)是一種元編程機制,廣泛用於框架設計、代碼增強(如 AOP)、參數校驗、權限控制、日誌埋點等場景。大型的框架工具,如Spring、MyBatis、Hibernate 都大量使用註解機制,一些我們熟知的第三方工具(Hutool、Lombok)也都是用了大量的註解機制來增強和擴展應用。
1 關於自定義註解
一般來説,常見的自定義註解基本的語法包括
- @Target 註解使用的目標(類、方法、字段等)
- @Retention 保留策略(源碼、類文件、運行時)
- @Documented 是否包含在 Javadoc 中
- @Inherited 子類是否可以繼承父類上的註解
自定義註解一般的獲取方式是結合反射讀取註解,而在使用方向上結合 Spring AOP 使用註解是最多的,Spring已經應用在各類Java語言中的架構中。
2 Retention註解
【定義】
@Retention 是一個元註解(Meta‐annotation),來源於java.lang.annotation.Retention,用來標記一個註解自身應當被保留到哪個階段(源碼/字節碼/運行時),他只能用在其他註解的定義上。
一張圖來看下Retention註解RetentionPolicy中的參數。
【參數詳解】
RetentionPolicy 定義了註解(@interface)在 Java 程序中的保留級別或者説是保留策略,也就是註解信息在什麼階段可見、可用。它包含三個枚舉值SOURCE、CLASS、RUNTIME:
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
SOURCE:註解僅在源代碼中可見,編譯時即被丟棄,生成的 .class 文件和運行時都不可見。CLASS:註解在編譯後保存在 .class 字節碼文件中,但 JVM 加載類時並不保留(即默認行為)。RUNTIME:註解在編譯後保存在 .class 文件中,並且在運行時通過反射依然可訪問。案例測試為了更好的理解Retention註解,可以參看下代碼示例。
package com.liu.aop;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
/**
* <p> RetentionTest </p>
* class
* TODO
*
* @author Aion.Liu
* @version v1.0.0
* @since 2025/5/21 09:07
*/
public class RetentionTest {
@SourceOnly
@ClassOnly
@RuntimeVisible
public void test() {
}
public static void main(String[] args) throws Exception {
// 僅 RuntimeVisible 會被反射讀取到
Method m = RetentionTest.class.getMethod("test");
System.out.println("SOURCE present: " + m.isAnnotationPresent(SourceOnly.class));
System.out.println("CLASS present: " + m.isAnnotationPresent(ClassOnly.class));
System.out.println("RUNTIME present:" + m.isAnnotationPresent(RuntimeVisible.class));
}
}
// 僅源碼階段
@Retention(RetentionPolicy.SOURCE)
@interface SourceOnly { }
// 編譯後保留,但運行時不可見
@Retention(RetentionPolicy.CLASS)
@interface ClassOnly { }
// 運行時可見
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeVisible { }
打印結果如下/xxx/jdk1.8.0_321.jdk/Contents/Home/bin/java -javaagent:/……次數省略不必要的信息。
SOURCE present: false
CLASS present: false
RUNTIME present:true
注意⚠️:
只有標註了 @Retention(RetentionPolicy.RUNTIME) 的註解,才可以通過反射 API(Annotation[], getAnnotation() 等)讀取。
- 默認策略:如果沒有聲明 @Retention,註解的默認保留策略是 CLASS。
- 性能考慮:過多 RUNTIME 註解可能增加運行時反射成本,慎用。
- 兼容性:使用 SOURCE 策略時,IDE 和編譯器插件需同時生效,否則註解處理器可能無法觸發。
3 Target註解
定義@Target 是用來指定自定義註解可以應用於哪些Java 元素的元註解(Meta-annotation),它定義了註解的作用範圍,幫助編譯器在錯誤的地方使用註解時給出提示。一張圖來看下Target註解ElementType中的參數(注意:圖中包括JDK 1.5 - JDK16之間註解參數值)。
參數詳解
@Target 註解參數是ElementType,他是一個枚舉值,ElementType 定義了所有可用的目標類型,一共12種(截止到JDK21,)。
public enum ElementType {
TYPE,
FIELD,
METHOD,
PARAMETER,
CONSTRUCTOR,
LOCAL_VARIABLE,
ANNOTATION_TYPE,
PACKAGE,
TYPE_PARAMETER, // JDK 8
TYPE_USE, // JDK 8
MODULE, // JDK 9
RECORD_COMPONENT; // JDK 16
}
注意⚠️
在使用時可以存在並存的情況,例如 METHOD 和 PARAMETER可以同時應用。案例測試Target註解只能應用在類和方法上面,在字段或參數上使用會編譯報錯。
// 自定義註解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {
String action();
}
// 應用在類名稱上
@Audit(action = "UserService")
public class UserService { ... }
public class OrderService {
// 應用在方法上
@Audit(action = "createOrder")
public void create() { ... }
}
4 Documented註解
定義
在 Java 中,@Documented 是一個元註解(meta-annotation),用於指示某個註解類型是否應該被包含在 Javadoc 等生成的文檔中。它是 Java 註解機制的一部分,定義在 java.lang.annotation 包中。
可從源代碼中查看定義,當定義一個自定義註解,並在其上添加了 @Documented,任何使用了該註解的地方,在生成 Javadoc 時都會包含該註解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
案例測試
// 1. 定義時使用Documented註解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ApiDescription {
String value();
}
// 2. 在類中使用自定義註解
@ApiDescription("註解描述。")
public class UserController {
}
// 3. 在shell中執行命令,生成的 HTML 文檔中將包含註解 @ApiDescription 的信息。
javadoc -d doc UserController.java
5 Inherited註解
元註解(meta-annotation),它控制一個註解是否會被子類繼承。
- @Inherited 只能用在自定義註解的定義上;
- 它表示:如果某個類使用了某個帶有@Inherited 的註解,那麼它的子類也會自動繼承該註解;
- 僅對類(class)有效,對方法、字段、構造器等無效。
總結
使用範圍自定義註解一般結合 Spring Boot + AOP 實現:
- 登錄校驗:@LoginRequired
- 權限控制:@Permission("admin")
- 接口限流:@RateLimit(limit = 5, seconds = 60)
- 日誌埋點:@TrackEvent(name = "點擊提交")
上述這些也都是在Java語言中,使用Spring框架使用的常用方式。