Java 5 (JDK 1.5) 引入 Annotation,將 **配置從 XML 解放出來,內嵌於代碼結構中**,實現「配置即代碼、代碼即文檔」。核心價值:
@interface 語法詳解package com.demo.anno;
import java.lang.annotation.*;
@Target(ElementType.METHOD) // 作用目標:方法
@Retention(RetentionPolicy.RUNTIME) // 保留策略:運行期可見
@Documented // 納入 Javadoc
@Inherited // 子類繼承 (僅對 TYPE 生效)
public @interface SimpleAnnotation {
String value() default ""; // 單一屬性慣命名 value,支援默認值
int priority() default 0; // 基本類型屬性
String[] tags() default {}; // 陣列屬性
EnumType mode() default EnumType.NORMAL; // 枚舉屬性
Class>[] groups() default {}; // Class 類型屬性
}
| 元註解 | 核心屬性 | 取值範圍/含義 | 影響階段 |
|---|---|---|---|
@Target | value: ElementType[] | TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE (JDK 8+) | 編譯期驗證使用位置 |
@Retention | value: RetentionPolicy | SOURCE (僅源碼), CLASS (字節碼, 默認), RUNTIME (運行期反射可見) | 類文件存儲與載入 |
@Documented | 無 | 標記納入 Javadoc 生成文檔 | 文檔生成 |
@Inherited | 無 | 子類自動繼承父類的此註解 (僅 TYPE 類型生效) | 反射查詢 getAnnotations() |
@SimpleAnnotation(value = "核心業務", priority = 1, tags = {"交易", "核心"}, mode = EnumType.CRITICAL)
public void processCoreTransaction() { ... }
// 單一 value 屬性可省略屬性名
@SimpleAnnotation("簡單標記")
public void simpleMethod() { ... }
// 陣列單元素可省略大括號
@SimpleAnnotation(tags = "單標籤")
public void singleTag() { ... }
AnnotatedElement APIpublic class AnnotationProcessor {
public static void process(Class> clazz) {
// 1. 類級註解
if (clazz.isAnnotationPresent(SimpleAnnotation.class)) {
SimpleAnnotation anno = clazz.getAnnotation(SimpleAnnotation.class);
System.out.println("類註解: " + anno.value());
}
// 2. 方法級註解
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(SimpleAnnotation.class)) {
SimpleAnnotation anno = method.getAnnotation(SimpleAnnotation.class);
System.out.printf("方法 %s: value=%s, priority=%d%n",
method.getName(), anno.value(), anno.priority());
// 執行業務邏輯 (示例)
try {
method.invoke(clazz.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
關鍵 API 對比:
getAnnotation(Class):獲取指定類型註解 (含繼承,受 @Inherited 影響)getAnnotations():獲取所有繼承的運行期註解getDeclaredAnnotations():僅獲取聲明在本元素上的註解 (不含繼承)| 註解 | 包 | 用途 | 典型場景 |
|---|---|---|---|
@Override | java.lang | 標記重寫父類方法,編譯器驗證簽名匹配 | 防止拼寫錯誤導致誤為新方法 |
@Deprecated | java.lang | 標記不推薦使用,編譯器產生警告、Javadoc 標識 | API 演進兼容期 |
@SuppressWarnings | java.lang | 抑制編譯器特定警告 | unchecked, rawtypes, deprecation, unused |
// 商標註解
public @interface Trademark {
String name();
String owner();
}
// 授權註解:嵌套商標陣列
@Target({ElementType.TYPE, ElementType.PACKAGE})
@Retention(RetentionPolicy.RUNTIME)
public @interface License {
String name();
String notice();
boolean redistributable();
Trademark[] trademarks(); // 嵌套註解陣列
}
// 使用
@License(
name = "Apache 2.0",
notice = "開源協議",
redistributable = true,
trademarks = {
@Trademark(name = "Mercedes", owner = "Daimler"),
@Trademark(name = "Daewoo", owner = "GM Korea")
}
)
public class ProductCatalog { }
javax.annotation.processing.Processor (或繼承 AbstractProcessor)@SupportedAnnotationTypes、@SupportedSourceVersionprocess(Set extends TypeElement>, RoundEnvironment) 中遍歷被註解元素Filer 生成新源文件 (.java)、類文件 (.class)、資源文件META-INF/services/javax.annotation.processing.Processor 註冊@Data、@Getter、@Builder 等標記目標類RUNTIME;純編譯期驗證/代碼生成用 SOURCE;默認 CLASS 適合字節碼分析工具。LOCAL_VARIABLE 註解無法反射獲取)@Documented + 詳細 Javadoc,註解即 API 契約Java 註解從「配置外部化」走向「語義內嵌」,經歷了 **運行期反射驅動 (Spring 2.x/JPA 1.0)** → **編譯期處理代碼生成 (Lombok/MapStruct/Dagger)** → **類型註解強化類型系統 (JDK 8 @NonNull/@CheckForNull)** 三代演進。掌握 **元註解語義、反射 API 邊界、APT 處理器模型、標準註解契約**,是構建現代 Java 框架、理解主流生態運作機制的核心鑰匙。