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

Java 註解深度解析:從元註解到 ButterKnife 原理

     編輯:Java 綜合教學

Java 註解深度解析:從元註解到 ButterKnife 原理

Java 註解(Annotation)是 JDK 5 引入的元資料機制,廣泛應用於框架開發、編譯檢查、程式碼生成等場景。本文系統梳理註解基礎、元註解、自定義註解、註解處理器,並深入剖析 ButterKnife 註解框架的編譯時處理原理。

什麼是註解?

註解是一種標記,像 @Override、@Target 等,它們不直接影響程式碼邏輯,但提供給編譯器、工具或執行期反射使用。註解資訊保留在 .class 檔案中(視保留策略而定),類似 C 語言的巨集但更強大,基於反射機制實現。

1. 元註解

元註解是用來修飾其他註解的註解,Java 內建四種:

  • @Target:指定註解可應用的程式元素類型(如 METHOD、FIELD、TYPE 等)。
  • @Retention:指定註解保留階段:
    • RetentionPolicy.RUNTIME:執行期可透過反射獲取。
    • RetentionPolicy.CLASS(預設):編譯期保留在 class 檔,執行期不可見。
    • RetentionPolicy.SOURCE:僅原始碼階段存在,編譯時丟棄。
  • @Documented:標示該註解應被 Javadoc 記錄。
  • @Inherited:標示該註解具有繼承性,子類別會繼承父類別的此註解。

2. 自定義註解

定義一個水果名稱註解 @FruitName,作用於欄位,執行期保留:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitName {
    String value() default "";
}

使用範例:

public class Apple {
    @FruitName("Apple")
    String appleName;

    public void displayAppleName() {
        TraceLog.i(appleName); // 此處 appleName 為 null,因為註解本身不自動賦值
    }
}

註解只是標記,需透過註解處理器讀取並賦值。

3. 註解處理器(反射方式)

透過反射掃描類別欄位,讀取註解屬性並設定值:

public class FruitInfoUtils {
    public static void getFruitInfo(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(FruitName.class)) {
                FruitName fruitName = field.getAnnotation(FruitName.class);
                String name = fruitName.value();
                // 此處可進一步將 name 設定回 field(需 field.setAccessible(true))
                TraceLog.i(name);
            }
        }
    }
}

4. Android 註解框架解析:ButterKnife 原理

ButterKnife 等框架利用編譯時註解處理器(APT),在編譯階段生成輔助類別,避免執行期反射開銷。

註解定義

@Retention(CLASS)
@Target(FIELD)
public @interface BindString {
    @StringRes int value();
}

註解處理器核心流程

  1. 掃描:RoundEnvironment 獲取所有標註 @BindString 的元素。
  2. 驗證與解析:檢查欄位型別是否為 String,提取資源 ID 與欄位名。
  3. 建立綁定資訊:將解析結果封裝為 FieldResourceBinding,存入 Map。
  4. 生成程式碼:BindingClass.brewJava() 使用 JavaPoet 生成 ViewBinder 實作類別,寫入 .java 檔案。
// 核心生成邏輯片段
JavaFile brewJava() {
    TypeSpec.Builder result = TypeSpec.classBuilder(generatedClassName)
        .addModifiers(PUBLIC);
    // ... 繼承關係、bind 方法、unbinder 類別生成 ...
    return JavaFile.builder(packageName, result.build())
        .addFileComment("Generated code from Butter Knife. Do not modify!")
        .build();
}

關鍵點總結

  • APT 在編譯期多輪處理,生成新 Java 程式碼,非執行期反射。
  • 所有註解資訊彙總於 targetClassMap,最終由 process() 方法觸發程式碼生成。
  • JDK 6 起標準化 javax.annotation.processing API,支援自定義 Processor。

參考:ButterKnife 原始碼

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved