註解提供了「聲明式元資料」能力,讓工具/框架在編譯期、類載入期、運行期讀取程式碼附加語義,實現代碼生成 (Lombok, MapStruct)、配置替代 XML (Spring, JPA)、靜態分析 (Checker Framework)、運行時行為注入 (AOP, 驗證)。JDK 5 引入統一註解機制,核心包含:定義語法、聲明語法、反射讀取 API、編譯器插件 (Annotation Processor)。
| 元註解 | 作用 | 關鍵屬性值 | 說明 |
|---|---|---|---|
@Retention | 生命週期 | SOURCE (編譯期丟棄)、CLASS (class 檔保留,VM 忽略,預設)、RUNTIME (運行期反射可讀) | 需反射讀取必須用 RUNTIME |
@Target | 作用目標 | TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、LOCAL_VARIABLE、ANNOTATION_TYPE、PACKAGE、TYPE_PARAMETER、TYPE_USE (JDK8+) | 可組合,如 {METHOD, FIELD} |
@Inherited | 繼承性 | 無屬性 | 被標註的註解類型可被子類繼承 (僅限 TYPE 目標) |
@Documented | 文檔化 | 無屬性 | 標註此元註解的註解會出現在 Javadoc 中 |
@Repeatable (JDK8+) | 可重複 | value = 容器註解類型 | 允許同一位置多次使用同一註解 |
@Native (JDK8+) | 原生常量 | 無屬性 | 提示註解值可能被原生代碼 (JNI) 讀取 |
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuditLog {
String value() default ""; // 單一元素慣用 value()
String operator() default "system"; // 一般元素
int level() default 1; // 基本型別
Severity severity() default Severity.INFO; // 列舉類型
Class<?>[] relatedEntities() default {}; // 類別陣列
// 不允許:方法參數、throws 子句、泛型元素 (除 Class)
}
enum Severity { INFO, WARN, ERROR }
@AuditLog(value = "用戶登入", operator = "auth-service", severity = Severity.INFO)
public void login(String username) { ... }
@AuditLog("資料刪除") // 省略 value() 名稱
public void delete(long id) { ... }
| 註解 | 用途 | 適用階段 |
|---|---|---|
@Override | 編譯期檢查方法確實覆寫父類/介面方法 | SOURCE |
@Deprecated | 標記不建議使用,編譯器警告 | RUNTIME |
@SuppressWarnings | 抑制特定編譯器警告 | SOURCE |
@SafeVarargs (JDK7+) | 標記方法/建構子不執行不可靠的可變參數操作 | RUNTIME |
@FunctionalInterface (JDK8+) | 標記函數式介面 (單一抽象方法),編譯器驗證 | RUNTIME |
實作 javax.annotation.processing.Processor,在編譯期掃描註解、生成新原始檔/資源/類檔。典型應用:
@Getter/@Setter/@Builder 生成樣板代碼。@Mapper 生成型別安全映射實作。@Component/@Module 生成依賴注入代碼。@Entity/@Dao 生成 SQLite 操作代碼。
@SupportedAnnotationTypes("com.example.AuditLog")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class AuditLogProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element elem : roundEnv.getElementsAnnotatedWith(AuditLog.class)) {
AuditLog ann = elem.getAnnotation(AuditLog.class);
// 使用 Filer 生成代碼/資源/類
// processingEnv.getFiler().createSourceFile(...)
}
return true; // 聲明已處理
}
}
註冊:在 META-INF/services/javax.annotation.processing.Processor 列出處理器全限定名。
Method method = MyClass.class.getMethod("login", String.class);
if (method.isAnnotationPresent(AuditLog.class)) {
AuditLog ann = method.getAnnotation(AuditLog.class);
System.out.println(ann.value() + " | " + ann.severity());
}
// 獲取所有註解 (含元註解)
Annotation[] anns = method.getAnnotations(); // 含繼承
Annotation[] declaredAnns = method.getDeclaredAnnotations(); // 僅本類聲明
@Transactional 只管事務邊界)。@Documented 讓 Javadoc 保留。註解是 Java 元編程的基石。掌握「定義→元註解配置→處理器/反射消費」完整鏈路,能讓你從框架使用者進階為框架建設者,構建極具表達力的領域特定語言 (DSL) 於 Java 之上。