承接入門篇,本文深入剖析 Annotation 核心定義、語法細節、反射存取機制、自訂型別設計模式、四大標準 Meta-Annotation 與實務決策指南。
Annotation 將任意資訊 (中繼資料) 以 name=value 結構關聯至程式元素 (套件、類別、方法、欄位、參數、區域變數)。鐵律:Annotation 不影響程式執行語義,增刪不改變行為。JVM 忽略 Annotation,唯有配套工具 (編譯器、APT、執行期框架) 解讀並產生作用。JDK bin/apt 提供處理框架,掃描原始碼並調用自訂處理器。
@Type(name=value),行為似修飾符。@interface),定義名稱、成員、預設值。反射讀取時回傳實作該介面的物件。default 預設值。@Override)。@Target、@Retention)。SOURCE (捨棄)、CLASS (存 Class 不可反射、預設)、RUNTIME (載入時讀取、可反射)。Marker 型別,標註方法斷言覆寫父類。拼錯方法名即編譯錯誤,極佳防呆。
Marker 型別,標註過時 API,編譯器警告。具「傳染性」:繼承/覆寫過時元素仍警告。區別於 @deprecated javadoc tag (文檔用),未來版本將移除編譯器對 tag 的支援,宜改用 Annotation。
單一成員 String[] value,值為 -Xlint 警告名。語法簡寫:@SuppressWarnings({"unchecked","fallthrough"}) 或單值 @SuppressWarnings("unchecked")。
@Type(member=value, ...),成員順序任意。@Reviews 包含 @Review[])。允許:基本型別、String、Class、Enum、Annotation Type、上述型別陣列。範例:@UncheckedExceptions({IllegalArgumentException.class, StringIndexOutOfBoundsException.class})。
package-info.java 套件宣告前,編譯產生 package-info.class 佔位介面。SOURCE),方法參數可保留至 Class 或 RUNTIME。預設值存於 Annotation Type 的 Class 檔,不編譯進使用端的 Annotation。修改 Annotation Type 預設值,所有未顯式指定該成員的現有 Annotation 即時生效 (無需重編譯使用端)。
AnnotatedElement 介面 (由 Package、Class、Method、Constructor、Field 實作) 提供:
isAnnotationPresent(Class):檢查 Marker Annotation。getAnnotation(Class):取得 Annotation 物件 (實作對應介面),透過方法呼叫讀取成員值。範例:讀取 @Reviews 陣列成員並逐一印出 grade、reviewer、comment。
@interface 隱含繼承 java.lang.annotation.Annotation。default (不可為 null)、不可泛型 (除 Class<?>)。// Marker
@Retention(RUNTIME)
public @interface Unstable {}
// Single-value (簡寫適用)
public @interface Author { String value(); }
// 複雜巢狀
@Retention(RUNTIME)
public @interface Reviews { Review[] value(); }
public @interface Review {
enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY }
Grade grade();
String reviewer();
String comment() default "";
}
// 泛型限制陣列
public @interface UncheckedExceptions {
Class extends RuntimeException>[] value();
}
成員 ElementType[] value,限定適用程式元素類型。若未指定,視為通用;指定後編譯器強制檢查 (如 @Override 加 @Target(METHOD) 禁止修飾類別)。
成員 RetentionPolicy value (SOURCE/CLASS/RUNTIME),控制生命週期。預設 CLASS。
Marker,指示該 Annotation 屬公開 API,應被 javadoc 等工具文檔化。
Marker,標註類別被繼承時,子類自動繼承該 Annotation。限制:不從介面繼承、方法不從覆寫方法繼承。RUNTIME 保留時,反射查詢會遞迴搜尋父類鏈。
Annotation (Metadata) 方式:適合小型專案、表數少 (<100)、無專職映射人員,開發速度快。缺點:領域模型與映射資訊耦合,測試需分離技術。
Mapping File (XML) 方式:適合中大型專案、專職映射團隊,降低資源爭用、提升彈性。PetStore 案例採 Annotation 節省時間。
資料庫遷移:映射層應隔離底層變更。PetStore 從商用 DB 遷移 PostgreSQL 無需改映射 (雙方支援 Sequence),但主鍵生成策略區域通常需重寫。
本地端設計 (同 JVM) 優勢多 (參考《J2EE Development without EJB》)。若需遠端,可加 Remote Facade:轉換領域模型與 DTO、委派 Service Facade。實作為 Remote Stateless Session Bean,代價是額外 DTO 層與轉換邏輯,換取乾淨介面與鬆耦合。
OptimisticLockException 僅在 Proposed Final Draft 出現,暫仍依賴提供者專屬例外 (如 Hibernate StaleObjectStateException)。