Spring 註解處理

最後更新: 2025-09-25 03:47:26 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

Spring 將註解用作宣告式中繼資料,用於元件探索、組態、相依性注入、交易、Web 要求對映,以及其他框架服務。Java 反射公開原始註解;Spring 則增加了用於組合註解、別名、搜尋策略,以及無需載入類別即可讀取中繼資料的一致模型。

Java 註解語意 #

註解宣告本身是介面,其隱含的超級介面是 java.lang.annotation.Annotation:

java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Audited {
}

當類別上直接存在@Audited時,執行階段反射可以檢查它。@Retention、@Target和@Documented等中繼註解用來描述註解宣告,但不會導致任意的傳遞式查詢。

SOURCE、CLASS和RUNTIME保留策略決定中繼資料只保留在原始碼中、保留在類別檔案中,或可透過反射取得。@Target限制宣告位置或型別使用位置。可重複註解透過@Repeatable指定容器;反射和Spring可以公開邏輯上的重複註解,也可以公開其實體容器。

@Inherited是一個範圍有限的例外。當註解宣告帶有@Inherited時,某些Class查詢方法可以在超類別上找到繼承的註解。此規則不適用於介面,也不涵蓋方法或欄位,並且不同於Spring的組合註解模型。

組合註解 #

Spring會解譯放置在其他註解宣告上的註解。例如,@Service使用@Component作為中繼註解:

java
@Component
public @interface Service {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

因此,使用@Service註解的類別會成為元件候選者,儘管一般反射不會回報該類別直接存在@Component。應用程式可以使用相同機制建立領域專用註解:

java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
@Transactional(readOnly = true)
public @interface ReadOnlyService {
}

這是一種程式設計模型,而不是Java語言規則。Spring程式碼必須執行走訪、循環偵測、篩選和屬性合成。

合併註解 #

MergedAnnotations是Spring用於直接存在、繼承存在和中繼存在註解的核心查詢模型。它的SearchStrategy控制要走訪哪些型別關係:

  • DIRECT僅檢查元素上直接宣告的註解。
  • INHERITED_ANNOTATIONS遵循Java的@Inherited規則。
  • SUPERCLASS搜尋超類別階層。
  • TYPE_HIERARCHY搜尋超類別和已實作的介面。

應使用與框架功能相符的最窄策略。廣泛的階層搜尋可能產生僅憑Java反射無法公開的中繼資料。

MergedAnnotation<T>表示一個已探索的註解,以及它的距離、彙總位置、來源和合成屬性。合成處理會解析透過@AliasFor宣告的Spring別名關係。因此,它可以在組合註解及其中繼註解之間呈現一致的屬性檢視。

別名宣告必須保持一致。在同一個註解內,互為別名的屬性必須具有相容的回傳型別和預設值。跨註解別名必須識別中繼註解階層中可到達的屬性。

中繼資料抽象 #

Spring將一般的帶註解元素中繼資料與類別層級和方法層級專用中繼資料分離:

AnnotAantniootnaMteetdaTdyapteaMetaMdeatthaodMetadata
  • AnnotatedTypeMetadata公開型別和方法共用的註解查詢。
  • AnnotationMetadata增加類別資訊以及對帶註解方法的存取。
  • MethodMetadata增加宣告類別、回傳型別和方法屬性。

以反射為基礎的實作會檢查已載入的類別。類別讀取實作使用位元組碼中繼資料,使元件掃描和組態解析等基礎設施能夠在不初始化類別的情況下檢查類別。避免載入類別可以防止靜態初始化副作用,並允許在所有選用相依性都可解析之前探索中繼資料。

對於已載入的類別,請使用受支援的內省入口點:

java
AnnotationMetadata metadata =
    AnnotationMetadata.introspect(ExampleConfiguration.class);

boolean configuration =
    metadata.hasAnnotation(Configuration.class.getName());

框架內部則可以從MetadataReaderFactory取得MetadataReader,以讀取.class資源。

組態類別處理 #

ConfigurationClassParser在處理@Configuration、元件掃描、匯入、屬性來源、條件和@Bean方法時使用類別中繼資料。@Import可以提供組態類別、選取器或註冊器。@Conditional實作可以在適用的解析階段或Bean註冊階段否決組態。 整體流程如下:

  1. 探索候選Bean定義。
  2. 讀取類別中繼資料。
  3. 判定每個候選者是否為組態類別。
  4. 評估條件,並走訪匯入項目和元件掃描。
  5. 探索Bean方法並註冊產生的Bean定義。

註解解析描述定義;後續的Bean工廠階段負責具現化和初始化Bean。保持這些階段彼此分離,可以解釋為何找到註解不會立即執行被註解的行為。

API選擇 #

當直接的Java註解語意已經足夠時,使用Java反射。實作必須遵循Spring組合註解或別名的基礎設施時,使用MergedAnnotations。在容器啟動期間必須檢查類別層級和方法層級中繼資料時,使用AnnotationMetadata或MetadataReader,尤其是在載入類別尚為時過早的情況下。

應用程式程式碼通常應使用較高層級的Spring功能,例如元件掃描、交易管理或MVC對映,而不是重新實作註解搜尋規則。

評論

在 GitHub 上閱讀或評論(發表評論需要登入)