註解 是 Java 5 引入的元數據機制,讓代碼「自我描述」,成為 Spring、Hibernate、MyBatis、JUnit 等框架實現聲明式編程的基石。本文系統講解註解定義語法、四大元註解、保留策略、目標範圍、屬性默認值、繼承規則,並通過反射實戰演示運行時解析。
// 定義註解:interface 前加 @
public @interface Author {
String name(); // 必填屬性,無默認值
int age() default 18; // 可選屬性,有默認值
String[] tags() default {}; // 數組屬性
}
// 特殊約定:唯一屬性名為 value,使用時可省略屬性名
public @interface Description {
String value(); // 使用:@Description("核心業務邏輯")
}
// 使用示例
@Author(name = "張三", age = 25, tags = {"core", "api"})
@Description("用戶服務實現")
public class UserService { /* ... */ }
元註解用於修飾註解定義,決定註解「能用在哪、存活多久、是否繼承、是否文檔化」。
| 元註解 | 作用 | 關鍵屬性/取值 | 默認值 |
|---|---|---|---|
| @Target | 限制註解可應用的程序元素 | ElementType[] value()TYPE(類/接口/枚舉)、FIELD(字段)、METHOD(方法)、PARAMETER(參數)、CONSTRUCTOR(構造器)、LOCAL_VARIABLE(局部變量)、ANNOTATION_TYPE(註解)、PACKAGE(包)、TYPE_PARAMETER(類型參數)、TYPE_USE(類型使用) | 所有類型均可用 |
| @Retention | 指定註解生命週期(保留策略) | RetentionPolicy value()SOURCE:源碼級,編譯器丟棄 (如 @Override、@SuppressWarnings) CLASS:字節碼級,JVM 加載但不保留,**默認值** RUNTIME:運行時級,反射可見,**框架註解必須設置** | CLASS |
| @Documented | 標記註解包含在 Javadoc 中 | 無屬性 | 不文檔化 |
| @Inherited | 允許子類繼承父類註解 | 無屬性 限制:僅對 類 註解有效;不適用於接口實現;只查詢直接父類,不遞歸。 | 不繼承 |
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) // 框架掃描必須 RUNTIME
@Documented
@Inherited // 僅類層級生效
public @interface ApiOperation {
String value() default ""; // 接口描述
String[] tags() default {}; // 分組標籤
String author() default "unknown"; // 負責人
int version() default 1; // 版本號
boolean deprecated() default false; // 是否廢棄
}
只有 @Retention(RUNTIME) 的註解才能通過反射讀取。
public class AnnotationParser {
public static void parse(Class> clazz) {
// 1. 類級註解
if (clazz.isAnnotationPresent(ApiOperation.class)) {
ApiOperation api = clazz.getAnnotation(ApiOperation.class);
System.out.printf("Class: %s | Desc: %s | Author: %s | Tags: %s%n",
clazz.getSimpleName(), api.value(), api.author(), Arrays.toString(api.tags()));
}
// 2. 方法級註解(含繼承查找)
for (Method method : clazz.getDeclaredMethods()) {
// getAnnotation 不查找父類/接口;需手動遞歸或用 AnnotatedElementUtils (Spring)
ApiOperation api = method.getAnnotation(ApiOperation.class);
if (api != null) {
System.out.printf(" Method: %s | Desc: %s | Deprecated: %s%n",
method.getName(), api.value(), api.deprecated());
}
}
// 3. 字段級註解
for (Field field : clazz.getDeclaredFields()) {
ApiOperation api = field.getAnnotation(ApiOperation.class);
if (api != null) {
System.out.printf(" Field: %s | Desc: %s%n", field.getName(), api.value());
}
}
}
public static void main(String[] args) {
parse(UserService.class);
}
}
@Target({TYPE, METHOD})。@Tag("core") 自動識別為單元素數組;多元素 @Tag({"core", "api"})。AnnotatedElementUtils.findMergedAnnotation 解決此限制。註解將配置從 XML 解放回代碼,實現「配置即代碼、代碼即文檔」。掌握 @Target 定界、@Retention 決定可見性、@Inherited 規範繼承、反射 API 解析元數據 這四大核心,即可讀懂主流框架源碼、開發自定義框架、構建代碼生成器。下一篇將實戰:基於註解實現輕量級參數校驗框架。