Java 5 引入註解機制,徹底改變了框架開發模式。Spring、Hibernate、JAX-RS 等主流框架大量採用註解替代繁瑣的 XML 配置,實現「配置即代碼」。本文系統講解註解基礎、元註解詳解、自定義註解實戰、內建註解應用,並演示反射解析註解的完整流程。
使用 @interface 關鍵字,方法即屬性,支援預設值:
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo {
String author() default "Pankaj";
String date();
int revision() default 1;
String comments();
}
| 元註解 | 作用 | 關鍵屬性 |
|---|---|---|
@Retention | 聲明生命週期 | SOURCE(源碼)、CLASS(編譯期,預設)、RUNTIME(運行期,反射必需) |
@Target | 限制標註位置 | TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, MODULE (Java 9+) |
@Documented | 納入 Javadoc | 標註的元素將出現在生成的 API 文檔中 |
@Inherited | 允許子類繼承 | 僅對 @Target(TYPE) 的類級註解有效 |
限制規則:註解屬性類型僅限基本型、String、Enum、Class、註解及其陣列;不接受參數;無拋出異常聲明。
| 註解 | 用途 | 保留策略 |
|---|---|---|
@Override | 標示覆寫父類方法,編譯器驗證簽名一致性 | SOURCE |
@Deprecated | 標示不建議使用,Javadoc 會標記,建議說明替代方案 | RUNTIME |
@SuppressWarnings | 抑制特定編譯警告(如 unchecked, deprecation) | SOURCE |
@SafeVarargs | Java 7+,抑制泛型可變參數堆污染警告 | RUNTIME |
@FunctionalInterface | Java 8+,標示函數式介面,編譯器強制單一抽象方法 | RUNTIME |
public class AnnotationExample {
@Override
@MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 1)
public String toString() { return "Overriden toString method"; }
@Deprecated
@MethodInfo(comments = "deprecated method", date = "Nov 17 2012")
public static void oldMethod() { System.out.println("old method, don't use it."); }
@SuppressWarnings({ "unchecked", "deprecation" })
@MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 10)
public static void genericsTest() throws FileNotFoundException {
List l = new ArrayList(); l.add("abc"); oldMethod();
}
}
前提:註解必須標註 @Retention(RetentionPolicy.RUNTIME)。
public class AnnotationParsing {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.journaldev.annotations.AnnotationExample");
for (Method method : clazz.getDeclaredMethods()) {
// 1. 檢查是否存在特定註解
if (method.isAnnotationPresent(MethodInfo.class)) {
// 2. 獲取所有註解(包含內建)
for (Annotation anno : method.getDeclaredAnnotations()) {
System.out.println("Annotation in Method '" + method + "' : " + anno);
}
// 3. 獲取指定註解實例,訪問屬性值
MethodInfo methodAnno = method.getAnnotation(MethodInfo.class);
if (methodAnno.revision() == 1) {
System.out.println("Method with revision no 1 = " + method);
}
}
}
}
}
關鍵反射方法:
isAnnotationPresent(Class):快速判斷getAnnotation(Class):獲取單個註解實例(含繼承)getDeclaredAnnotations():獲取直接聲明的所有註解(不含繼承)getAnnotations():獲取所有註解(含繼承)@Path, @GET 映射 REST 端點;Spring @Autowired, @Transactional 驅動 IOC/AOP;JPA @Entity, @Column 定義映射。@Test, @BeforeEach 控制測試生命週期。@CodeReview, @Todo 配合靜態分析工具追蹤技術債。RUNTIME;編譯期處理用 SOURCE;僅文檔用 CLASS(預設)。default 減少使用端樣板代碼。@Inherited 僅對類級註解生效,方法/欄位註解不會被子類繼承。註解是 Java 元編程的基石,從簡單的編譯器輔助到複雜的框架契約,掌握「定義-保留-目標-解析」全鏈路,能讓你深度理解主流框架運作原理,並具備開發領域專用框架的能力。記住:好的註解設計應該像標準庫一樣直觀、自描述、且有明確的語義契約。