1. 程式人生 > >Spring 註解程式設計之註解屬性別名與覆蓋

Spring 註解程式設計之註解屬性別名與覆蓋

前兩篇文章咱聊了深入瞭解了 Spring 註解程式設計一些原理,這篇文章我們關注註解屬性方法,聊聊 Spring 為註解的帶來的功能,屬性別名與覆蓋。

註解屬性方法

在進入瞭解 Spring 註解屬性功能之前,我們先看一個正常 Java 註解。

在註解中,屬性方法與其他類/介面方法寫法類似,但是存在一些區別。

註解屬性方法的返回型別僅限為八種基本型別(包裝類不支援),字串,class,enum,Annotation以及前面型別的陣列。

複習一下,java 八種基本型別分別為,byte(位元組型)、short(短整型)、int(整型)、long(長整型)、float(單精度浮點型)、double(雙精度浮點型)、boolean(布林型)、char(字元型)。

其次,註解屬性方法可以使用 default設定預設值。如果沒有設定預設值,宣告註解時必須顯式設定屬性,否則編譯將會出錯。

另外 Java 註解無法繼承類,也無法實現介面。

Spring 屬性方法特性

在 Spring 中,有一些註解,使用不同屬性方法,卻能到達相同結果。典型的如 RequestMapping

在 WEB 專案中,設定 url 路徑,我們可以在方法是這樣宣告:

    @RequestMapping("hello")
    public String helloAnnotation() {
    。。。。
    }

上面方法本質使用註解 value

屬性。當註解宣告時只需要設定一個方法時,如果屬性方法為 value,不需要使用 key=value 的語法,只需要直接設定屬性值即可。

另外也可以使用 path 屬性方法設定。

    @RequestMapping(path = "hello")
    public String helloAnnotation() {
    。。。。
    }

兩種方式,最後執行效果一致。

檢視 RequestMapping 註解原始碼,可以發現在 valuepath 屬性方法上使用 @AliasFor,並且兩個互相指向對方。

Spring 4.2 加入 @AliasFor

註解,並使用 @AliasFor 重新更新 RequestMapping等註解,為它們內部帶來了別名的功能。

@AliasFor 使用方式

在 Spring 中,@AliasFor 可以在同一註解中使用,使用方法如 RequestMapping 註解。

這種方式,帶來含義明確屬性方法。如 RequestMappingpath 屬性方法,這個屬性方法含義就比較明確,不同的人理解不會有偏差。而 value 屬性含義就不是很明確,不能一下子就將它真正含義產生聯絡。

日常開發中,我們也要避免 i,a,b 這些無意義的命名,儘量使用含義明確的命名。這樣利用維護程式碼的人理解。

第二點,同一註解屬性方法相互別名,這樣就相容之前版本用法。

RequestMapping 註解如果僅新增 path 屬性,然後根據其解析 url 路徑,這樣就會導致升級 Spring 版本過程,執行錯誤的。

一個好軟體版本需要時向前相容,如 JDK 8 相容 JDK 6一樣。

另外 @AliasFor 註解還可以作用與不同註解之前,典型的如 SpringBootApplication註解。

SpringBootApplication#scanBasePackages 別名與 ComponentScan#basePackages。設定前者間接為後者賦值。

Spring Boot 就是使用 @Aliasfor 與組合註解功能,使用 SpringBootApplication一個註解代替 ConfigurationEnableAutoConfigurationComponentScan

Spring 註解屬性覆蓋與別名

使用 @AliasFor 註解,可以做到別名的功能。

在 Spring 中別名可以分為以下幾類:

  1. 顯式別名(xplicit Aliases)
  2. 隱式別名(Implicit Aliases)
  3. 傳遞隱式別名(Transitive Implicit Aliases)

以上三類都需要滿足以下條件:

  1. 屬性型別相同
  2. 屬性方法必須存在預設值
  3. 屬性預設值必須相同

否則執行過程中將會出錯。

顯式別名

如果一個註解中的兩個成員通過 @AliasFor聲明後互為別名,那麼它們是顯式別名

顯示別名的關係如圖所示。

隱式別名

如果一個註解中的兩個或者更多成員通過@AliasFor宣告去覆蓋同一個元註解的成員值,它們就是隱式別名

隱式別名如圖所示。

上圖中,@Onename 屬性與 nameAlias 別名與 @Two nameAlias 屬性。由於 @One 註解中並未直接使用 @AliasFor,所以與 @One 註解隱式別名。

隱式別名類似於數學的等式。可以將其看做以下推導過程。

@[email protected]
@[email protected]
可以推匯出
@[email protected]

傳遞式隱式別名

如果一個註解中的兩個或者更多成員通過@AliasFor宣告去覆蓋元註解中的不同成員,但是實際上因為覆蓋的傳遞性導致最終覆蓋的是元註解中的同一個成員,那麼它們就是傳遞隱式別名。

傳遞式隱式別名如圖所示。

這種型別涉及了多個註解,@One#name別名了 @Two#nameAlias屬性,然後在 @One#nameAlias 屬性又別名了 @Three#nameAliasThree 屬性。然後由於 @Two#nameAlias又別名了 @Three#nameAliasThree 屬性,這就導致 @One#name@One#nameAlias 間接才生了關係。這種依靠傳遞性才生別名關係,稱為 傳遞式隱式別名。

隱式別名類似於數學的等式。大家也可以將其用上面等式推導。

屬性覆蓋

屬性覆蓋指的是註解的一個成員覆蓋另一個成員,最後兩者成員屬性值一致。

屬性覆蓋可以分為三類:

  1. 隱式覆蓋(Implicit Overrides)
  2. 顯示覆蓋(Explicit Overrides)
  3. 傳遞式顯式覆蓋(Transitive Explicit Overrides)

隱式覆蓋

當一個註解 @One 被元註解 @Two 標註,兩個註解存在同樣的屬性方法 name@Two#name 將會被 @One#name 屬性覆蓋。

兩個看似不來自不同註解的成員 name 指向了同一個成員 name。

顯示覆蓋

顯示覆蓋就比較簡單了,使用 @AliasFor 註解之後,就成為顯示覆蓋。

傳遞式顯式覆蓋

如果註解 @One#name 顯示覆蓋了 @Two#nameAlias,而 @Two#nameAlias 顯示覆蓋了 @Three#nameAlias,最後因為傳遞性,@One#name 實際覆蓋了@Three#nameAlias

總結

Spring 4.2 新增 @AliasFor註解,帶來一些特性。但是要注意的是僅僅存在 @AliasFor 不會執行任何語義別名。

底層原理可以參考 AnnotationUtilsAnnotatedElementUtils

幫助文件

  1. Attribute Aliases and Overrides
  2. 註解程式設計模型~~~~

另外歡迎加入 Java 極客技術知識星球,獲取最新 Java 技術。