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
註解原始碼,可以發現在 value
與 path
屬性方法上使用 @AliasFor
,並且兩個互相指向對方。
Spring 4.2 加入 @AliasFor
@AliasFor
重新更新 RequestMapping
等註解,為它們內部帶來了別名的功能。
@AliasFor
使用方式
在 Spring 中,@AliasFor
可以在同一註解中使用,使用方法如 RequestMapping
註解。
這種方式,帶來含義明確屬性方法。如 RequestMapping
,path
屬性方法,這個屬性方法含義就比較明確,不同的人理解不會有偏差。而 value
屬性含義就不是很明確,不能一下子就將它真正含義產生聯絡。
日常開發中,我們也要避免 i,a,b 這些無意義的命名,儘量使用含義明確的命名。這樣利用維護程式碼的人理解。
第二點,同一註解屬性方法相互別名,這樣就相容之前版本用法。
RequestMapping
註解如果僅新增 path
屬性,然後根據其解析 url 路徑,這樣就會導致升級 Spring 版本過程,執行錯誤的。
一個好軟體版本需要時向前相容,如 JDK 8 相容 JDK 6一樣。
另外 @AliasFor
註解還可以作用與不同註解之前,典型的如 SpringBootApplication
註解。
SpringBootApplication#scanBasePackages
別名與 ComponentScan#basePackages
。設定前者間接為後者賦值。
Spring Boot 就是使用 @Aliasfor
與組合註解功能,使用 SpringBootApplication
一個註解代替 Configuration
,EnableAutoConfiguration
,ComponentScan
。
Spring 註解屬性覆蓋與別名
使用 @AliasFor
註解,可以做到別名的功能。
在 Spring 中別名可以分為以下幾類:
- 顯式別名(xplicit Aliases)
- 隱式別名(Implicit Aliases)
- 傳遞隱式別名(Transitive Implicit Aliases)
以上三類都需要滿足以下條件:
- 屬性型別相同
- 屬性方法必須存在預設值
- 屬性預設值必須相同
否則執行過程中將會出錯。
顯式別名
如果一個註解中的兩個成員通過 @AliasFor
聲明後互為別名,那麼它們是顯式別名
。
顯示別名的關係如圖所示。
隱式別名
如果一個註解中的兩個或者更多成員通過@AliasFor
宣告去覆蓋同一個元註解的成員值,它們就是隱式別名
。
隱式別名如圖所示。
上圖中,@One
的 name
屬性與 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
間接才生了關係。這種依靠傳遞性才生別名關係,稱為 傳遞式隱式別名。
隱式別名類似於數學的等式。大家也可以將其用上面等式推導。
屬性覆蓋
屬性覆蓋指的是註解的一個成員覆蓋另一個成員,最後兩者成員屬性值一致。
屬性覆蓋可以分為三類:
- 隱式覆蓋(Implicit Overrides)
- 顯示覆蓋(Explicit Overrides)
- 傳遞式顯式覆蓋(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
不會執行任何語義別名。
底層原理可以參考 AnnotationUtils
與 AnnotatedElementUtils
。
幫助文件
- Attribute Aliases and Overrides
- 註解程式設計模型~~~~
另外歡迎加入 Java 極客技術知識星球,獲取最新 Java 技術。