首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

《Thinking in Java》筆記(20):註解 深度解析與處理器實戰

     編輯:Java 相關知識

Java 5 引入註解機制,將元數據與代碼統一,成為框架開發、編譯時檢查、代碼生成的基石。本文基於《Thinking in Java》第 20 章,系統梳理註解語法、元註解、屬性約束、處理器編寫、apt 工具、Visitor 模式應用及單元測試整合。

一、註解基礎語法

  • 語法上類似修飾符,可用於包、類、方法、字段、參數、局部變量、類型參數、類型使用(Java 8+)。
  • 編譯期類型檢查,編譯為 .class 文件,運行期可通過反射讀取(RetentionPolicy.RUNTIME)。

二、定義註解:關鍵元註解


@Target(ElementType.METHOD | ElementType.FIELD)  // 作用目標
@Retention(RetentionPolicy.RUNTIME)               // 生命週期:SOURCE/CLASS/RUNTIME
@Documented                                      // 納入 Javadoc
@Inherited                                       // 允許子類繼承(僅對類級註解有效)
public @interface MyAnnotation {
    String value() default "";                    // 默認屬性名為 value,單屬性可省略名稱
    int count() default 1;
    String[] tags() default {};                   // 數組屬性
    Class clazz() default Object.class;        // Class 類型屬性
    MyEnum enumVal() default MyEnum.V1;           // 枚舉類型
    // 不允許基本類型包裝器作為屬性類型(自動裝箱後可用)
}

元註解詳解

元註解作用可選值
@Target限定註解可應用位置ElementType 枚舉:TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE
@Retention限定註解保留階段SOURCE(源碼丟棄)、CLASS(編譯期保留,默認)、RUNTIME(運行期反射可見)
@Documented標記納入 Javadoc無
@Inherited子類自動繼承父類類級註解無(僅對 @Target(TYPE) 生效)
@RepeatableJava 8+ 允許同一位置重複使用需定義容器註解

三、屬性約束與默認值

  • 屬性類型限制:基本類型、String、Class、枚舉、註解類型、以上類型的一維數組。
  • 無 null 默認值(基本類型不可為 null),但可顯式指定 default "" 或 default null(針對 Class/枚舉/註解/數組)。
  • 單屬性命名為 value 時,使用註解可省略屬性名:@MyAnnotation("hello")。

四、編寫註解處理器

1. 運行期反射處理(最常用)


public class RuntimeProcessor {
    public static void process(Class clazz) {
        if (clazz.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation ann = clazz.getAnnotation(MyAnnotation.class);
            System.out.println("value=" + ann.value() + ", count=" + ann.count());
        }
        // 方法級處理
        for (Method m : clazz.getDeclaredMethods()) {
            if (m.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation ann = m.getAnnotation(MyAnnotation.class);
                // 業務邏輯:生成代碼、驗證、註冊路由等
            }
        }
    }
}

2. 編譯期處理:javax.annotation.processing (JSR 269)

實現 AbstractProcessor,在編譯階段掃描註解,生成源碼/資源文件/編譯錯誤/警告。優勢:零運行期開銷,可生成 Builder、DTO、Router 等樣板代碼。


@SupportedAnnotationTypes("com.example.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        for (Element elem : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
            // 使用 Filer 生成文件
            Filer filer = processingEnv.getFiler();
            // 例如生成 XXX_Generated.java
        }
        return true; // 聲稱已處理,不再傳遞給其他處理器
    }
}

註冊方式:META-INF/services/javax.annotation.processing.Processor 文件中寫入處理器全限定名。

3. 歷史工具 apt (Annotation Processing Tool)

  • JDK 5~7 專用,JDK 8+ 已移除,統一遷移至 JSR 269 API。
  • 基於源碼模型(Mirror API),不加載類,適合生成代碼。
  • 配合 Visitor 模式遍歷 AST,解耦操作與節點類型。

五、Visitor 模式在註解處理中的應用

當註解類型增多,處理邏輯分散在多個 if-else 中難以維護。引入 Visitor:

  • 定義 AnnotationVisitor 接口,每種註解一個 visitXxx 方法。
  • 處理器持有 Visitor 實例,遍歷元素時調用 accept(visitor)。
  • 新增註解處理只需擴展 Visitor 實現,無需修改核心遍歷邏輯(開閉原則)。

六、基於註解的單元測試整合

將測試代碼內嵌業務類,通過註解標記測試方法,運行期掃描執行:


public @interface Test { }

public class Calculator {
    public int add(int a, int b) { return a + b; }

    @Test
    private void testAdd() {          // 可測試 private 方法
        assert add(1, 2) == 3;
    }
}

// 測試運行器掃描 @Test 並執行
public class TestRunner {
    public static void run(Class clazz) throws Exception {
        for (Method m : clazz.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Test.class)) {
                m.setAccessible(true); // 突破 private
                m.invoke(clazz.newInstance());
            }
        }
    }
}

優勢:測試與被測代碼同文件,重構同步;可直接測試私有方法。缺點:生產代碼體積膨脹,現代實踐多採用獨立測試源碼集(JUnit/TestNG)。

七、註解不支持繼承

  • @interface 不能 extends 其他註解。
  • JLS 明確規定,未來版本也不計劃支持。
  • 通用元數據復用請用組合:定義基礎註解作為屬性類型,或使用 @Inherited 實現類級繼承語義。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved