1. 程式人生 > >《Spring 5官方文件》35. Spring註解程式設計模型

《Spring 5官方文件》35. Spring註解程式設計模型

原文連結 譯者:葉揚V

介紹

這篇文件是以Spring Framework 4.2作為框架基礎編寫的,但是,這篇文件是一份還在進行的工作。所以隨著時間推移,你會看到這份文件還在更新。

目錄

概要

這些年,Spring Framework已經頻繁的升級它可以支援的註解、元註解和組合註解。這篇文件旨在幫助開發者(Spring框架使用者、Spring核心框架開發者和Spring全家桶的成員專案開發者)開發和運用Spring註解。

這些是文件目標

這篇文件的主要目的包含以下內容:

  • 怎麼使用Spring註解。
  • 怎麼自定義組合註解。
  • 怎麼查詢Spring註解(或者說,理解Spring註解搜尋演算法的原理)。

這些並非文件目標

這篇文件並不介紹spring經典註解的語義和配置方法。如果想學習經典註解的細節,建議開發者查閱對應的Javadoc或者參考書籍的相應章節。

術語

元註解

元註解是一種標註在別的註解之上的註解。如果一個註解可以標註在別的註解上,那麼這個註解已然是元註解。例如,任何需要被文件化的註解,都應該被java.lang.annotation包中的元註解@Documented標註。

Stereotype註解

譯者注:保留Stereotype原生詞彙;可理解為模式化註解、角色類註解。

Stereotype註解是一種在應用中,常被用於宣告要扮演某種職責或者角色的註解。例如,@Repository

註解用於標註任何履行了repository職責角色的類(這種職責角色通常也會被稱為Data Access Object或者DAO)。

@Component是被Spring管理的元件的對應註解。任何標註了@Component的元件都會在spring元件掃描時被掃描到。同樣的,任何標註了 被元註解@Component標註過的註解 的元件,也會在Spring元件掃描時被掃描到。例如,@Service就是一種被元註解@Component標註過的註解。

Spring核心框架提供了一系列可直接使用的stereotype註解,包括但不限於@Component, @Service, @Repository, @Controller

, @RestController, and @Configuration@Repository, @Service等等註解都基於@Component的更精細化的元件註解。

組合註解

組合註解是一種被一個或者多個元註解標註過的註解,用以撮合多個元註解的特性到新的註解。例如,叫作@TransactionalService的註解就是被spring的@Transactional@Service共同標註過,而且它把@Transactional@Service的特性結合到了一起。另外,從技術上上講,@TransactionalService也是一個常見的Stereotype註解。

註解標註形式

一個註解無論是直接標註還是間接標註一個bean,這個註解在java8的java.lang.reflect.AnnotatedElement類註釋中所約定的含義和特性都不會有任何改變。
在Spring中,如果一個元註解標註了其它註解,其它註解標註了一個bean,那麼我們就說這個元註解meta-present(間接標註了)這個bean。以上文提到的@TransactionalService為例,我們可以說,@Transactional間接標註了任何一個標註過@TransactionalService的bean。

成員別名和覆蓋

Attribute Alias(成員別名)將註解的一個成員名變為另一個。一個成員有多個別名時,這些別名是平等可交換的。成員別名可以分為以下幾種。

  • 1. Explicit Aliases(明確的別名):如果一個註解中的兩個成員通過 @AliasFor聲明後互為別名,那麼它們是明確的別名。
  • 2. Implicit Aliases(隱含別名):如果一個註解中的兩個或者更多成員通過@AliasFor宣告去覆蓋同一個元註解的成員值,它們就是隱含別名。
  • 3. Transitive Implicit Aliases(傳遞的隱含別名):如果一個註解中的兩個或者更多成員通過@AliasFor宣告去覆蓋元註解中的不同成員,但是實際上因為覆蓋的傳遞性導致最終覆蓋的是元註解中的同一個成員,那麼它們就是可傳遞的隱含別名。

Attribute Override(成員覆蓋)是註解的一個成員覆蓋另一個成員。成員覆蓋可以分為以下幾種。

  • 1.Implicit Overrides(隱含的覆蓋):註解@One有成員A,註解@Two有也有成員A,如果@One本身是被元註解@Two標註的,那麼按照命名約定,註解@One中的成員A實際會覆蓋註解@Two中的成員A(用另一種方式說,兩個看似不來自不同註解的成員A指向了同一個成員A)。
  • 2.Explicit Overrides(明確的覆蓋):如果元註解的成員B通過@AliasFor宣告其別名為成員A,那麼成員A就是明確的覆蓋了成員B。
  • 3. Transitive Explicit Overrides(傳遞的明確覆蓋)如果註解@One中的成員A明確覆蓋了註解@Two中的成員B,而且成員B實際覆蓋了註解@Three中的成員C,那麼因為覆蓋的傳遞性,所以成員A實際覆蓋了成員C。

例子

Spring Composed

譯者注:Spring Composed專案是spring的一個社群專案,內含一些有特色的組合註解。可點選下文中的連結下載原始碼

Spring Composed專案是可以在在spring4.2.1和更高版本中使用的一系列組合註解。你可以在Spring MVC使用像@Get, @Post, @Put, 和@Delet這樣的註解,也可以在Spring MVC REST應用中使用@GetJson, @PostJson等等註解。

如果你確信已經學習了足夠深入的spring-composed例子,汲取了足夠多的靈感,然後你可以為spring-Composed專案貢獻出由你自定義的組合註解!

聲明瞭@AliasFor的成員

Spring4.2引入了用以支援宣告和查詢註解成員別名的第一個版本。@AliasFor註解可以用來在一個註解中為一對成員互相宣告別名,也可以在組合註解中為一個成員宣告一個指向另一個元註解成員的別名。

例如,來自spring-test模組的@ContextConfiguration註解現在是這樣定義的:

public @interface ContextConfiguration {

    @AliasFor("locations")
    String[] value() default {};

    @AliasFor("value")
    String[] locations() default {};

    // ...
}

同樣的,組合註解可以使用@AliasFor來覆蓋元註解的成員,這樣可以在組合註解中進行精細的操作,而且註解與元註解之間的關係更有層次。實際上,這也提供了為元註解成員起別名的可操作方案。

例如,我們可以使用自己想要的成員名稱去開發組合註解,然後再覆蓋元註解的成員。如下。
@ContextConfiguration
public @interface MyTestConfig {

    @AliasFor(annotation = ContextConfiguration.class, attribute = "value")
    String[] xmlFiles();

    // ...
}

FAQ

1) @AliasFor可以用於標註@Component@Qualifier`的成員變數嗎?

答案是不可以。

@Qualifier和stereotype註解(例如@Component, @Repository, @Controller,和其它定製的stereotype註解)不能被@AliasFor影響。原因是這些註解的成員變數在@AliasFor被髮明之前數年就已經存在了。因此,出於向後的相容性考慮,這些註解的成員變數就不能使用@AliasFor

附錄

使用@AliasFor的註解

在Spring4.2版本中,以下註解使用了@AliasFor來為它們的成員宣告別名。

  • org.springframework.cache.annotation.Cacheable
  • org.springframework.cache.annotation.CacheEvict
  • org.springframework.cache.annotation.CachePut
  • org.springframework.context.annotation.ComponentScan.Filter
  • org.springframework.context.annotation.ComponentScan`
  • org.springframework.context.annotation.ImportResource
  • org.springframework.context.annotation.Scope
  • org.springframework.context.event.EventListener
  • org.springframework.jmx.export.annotation.ManagedResource
  • org.springframework.messaging.handler.annotation.Header
  • org.springframework.messaging.handler.annotation.Payload
  • org.springframework.messaging.simp.annotation.SendToUser
  • org.springframework.test.context.ActiveProfiles
  • org.springframework.test.context.ContextConfiguration
  • org.springframework.test.context.jdbc.Sql
  • org.springframework.test.context.TestExecutionListeners
  • org.springframework.test.context.TestPropertySource
  • org.springframework.transaction.annotation.Transactional
  • org.springframework.transaction.event.TransactionalEventListener
  • org.springframework.web.bind.annotation.ControllerAdvice
  • org.springframework.web.bind.annotation.CookieValue
  • org.springframework.web.bind.annotation.CrossOrigin
  • org.springframework.web.bind.annotation.MatrixVariable
  • org.springframework.web.bind.annotation.RequestHeader
  • org.springframework.web.bind.annotation.RequestMapping
  • org.springframework.web.bind.annotation.RequestParam
  • org.springframework.web.bind.annotation.RequestPart
  • org.springframework.web.bind.annotation.ResponseStatus
  • org.springframework.web.bind.annotation.SessionAttributes
  • org.springframework.web.portlet.bind.annotation.ActionMapping
  • org.springframework.web.portlet.bind.annotation.RenderMapping

待發掘主題(譯者注:課後習題-_-!)

  • 記述 註解和標註了註解和元註解的類、介面、成員方法、成員變數、引數的通用搜索演算法。
    • 如果一個註解既是以註解又是以元註解的方式標註了一個元素會發生什麼呢?
    • 一個註解標註了@Inherited(包括自定義組合註解)後,是如何對搜尋演算法產生影響呢?
  • 記述 通過@AliasFor配置註解成員別名的技術原理。
    • 如果一個成員和它的別名都宣告在一個註解例項(成員和別名值相同或者不同時)中在技術上會發生什麼?
      • 較有代表性的一種情況是,一個AnnotationConfigurationException將會被丟擲。
  • 記述 組合註解的技術原理。
  • 記述 組合註解成員覆蓋元註解成員的原理。
    • 詳細記述 查詢成員的演算法原理:
      • 基於命名約定的間接覆蓋(換句話說,組合註解中有明確名字和型別的成員去覆蓋元註解的成員)
      • 使用@AliasFor來直接覆蓋
    • 如果一個成員和它的眾多別名中的一個在註解繼承的某一層級中被重新聲明瞭會發生什麼?哪個會生效?
    • 總之,成員變數宣告時的衝突是怎麼被解決的?