本文咱們來梳理一下Spring的那些註解,以下圖所示,大概從幾方面列出了Spring的一些註解:java
若是此圖看不清楚也沒事,請運行下面的代碼輸出全部的結果。spring
Spring目前的趨勢是使用註解結合Java代碼而不是配置來定義行爲、屬性、功能、規則和擴展點,所以梳理註解也是梳理Spring功能點的很好的方式,全面的梳理能夠補足咱們知識點的漏洞。跨域
查找全部註解bash
首先,咱們來建立一個項目,使用SPRING INITIALIZR生成一個引入Spring各類組件的項目模板,而後引入以下工具包:cookie
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
複製代碼
經過這個反射工具包,咱們能夠建立一個Spring Boot應用程序,以一行代碼打印出全部Spring框架的註解:app
import org.reflections.Reflections;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
@Component
public class ScanAnnotationRunner implements CommandLineRunner { @Override
public void run(String... args)
throws Exception {
new Reflections("org.springframework")
.getSubTypesOf(Annotation.class)
.stream()
.map(clazz->clazz.getName())
.sorted()
.forEach(System.out::println);
}
}
複製代碼
輸出結果這裏就不給出了,下面咱們逐一進行梳理其中的一些重要註解。負載均衡
有關注解框架
Java的Annotation註解(相似於C#的Attribute特性),說白了就是給代碼打上標籤的能力。咱們能夠配置這個標籤的保留階段,僅源代碼,源代碼+字節碼,源代碼+字節碼+運行時。經過引入註解,咱們能夠簡單快速賦予代碼生命力,大大提升代碼可讀性和擴展性。註解自己不具備任何能力,只是一個標籤,可是咱們能夠定義各類標籤而後實現各類標籤處理器來對類、方法、屬性甚至參數等進行功能擴展、功能開啓、屬性定義、行爲定義、規則定義、關聯處理、元數據定義等等。在實現各類框架的時候,咱們常常會自定義標籤方便框架使用者僅僅經過在合適的地方引入合適的註解來啓用(或自定義)框架的一些能力並應用到咱們的程序中。ide
不只僅是框架的做者會大量使用註解,在以前的系列文章中咱們也屢次自定義註解,咱們有經過定義@Metrics註解配合Spring AOP來爲程序啓動打點、日誌、異常等功能,咱們有經過定義@Sign註解配合Spring MVC的ResponseBodyAdvice進行數據簽名功能,咱們還常常會定義各類自定義註解配合Spring MVC的HandlerMethodArgumentResolver進行權限的校驗等等功能。採用這種模式,咱們的核心業務邏輯能夠保持清晰乾淨,經過註解配合AOP賦予代碼額外的能力。工具
你可能會說,註解仍是有侵入性,咱們須要耦合框架定義的那些註解,這個問題實際上是無解的,100%無侵入性也表明了可讀性的下降,代碼的功能和能力應當聚合在一塊兒,這也就是爲何Spring如今也不建議採用XML來作配置。Java核心類庫並無什麼註解,好在Spring已經有了大量註解,而Spring也變爲了Java開發的標準,因此其實咱們不少時候若是但願本身的框架(RPC啥的)徹底沒有侵入性的話能夠借用Spring的那些註解@Autowired、@Controller、@Service等註解,配合各類包的規範其實咱們能夠對目標元素的功能識別個八九不離十,徹底有可能實現0侵入的功能加強。
有關如何實現自定義註解不贅述,這裏咱們簡單回顧一下幾個元註解(註解的註解):
A. @Documented:將會在被此註解註解的元素的javadoc文檔中列出註解,通常都打上這個註解沒壞處
B. @Target:註解能被應用的目標元素,好比類、方法、屬性、參數等等,須要仔細思考
C. @Retention:僅在源碼保留,仍是保留到編譯後的字節碼,仍是到運行時也去加載,超過90%的應用會在運行時去解析註解進行額外的處理,因此大部分狀況咱們都會設置配置爲RetentionPolicy.RUNTIME
D. @Inherited:若是子類沒有定義註解的話,能自動從父類獲取定義了繼承屬性的註解,好比Spring的@Service是沒有繼承特性的,可是@Transactional是有繼承特性的,在OO繼承體系中使用Spring註解的時候請特別注意這點,理所固然認爲註解是能被子類繼承的話可能會引發沒必要要的Bug,須要仔細斟酌是否開啓繼承
E. @Repeatable:Java 8 引入的特性,經過關聯註解容器定義可重複註解,小小語法糖提升了代碼可讀性,對於元素有多個重複註解實際上是很常見的事情,好比某方法能夠是A角色能夠訪問也能夠是B角色能夠訪問,某方法須要定時任務執行,要在A條件執行也須要在B條件執行
F. @Native:是否在.h頭文件中生成被標記的字段,除非原生程序須要和Java程序交互,不然不多會用到這個元註解
如今咱們來從幾個方面逐一溫習一下Spring的那些經常使用的值得關注的註解。
Spring核心註解
A. 首先來看一下各類stereotype:按分類定義了由Spring管理的各類組件,@Controller定義表現層組件,@Service定義業務邏輯層組件,@Repository定義數據訪問層資源庫組件,@Component定義其它組件(好比訪問外部服務的組件),以前也說過了隨着這些註解功能無區別,可是對組件進行合適的分類意義重大,不只僅增長可讀性並且方便咱們經過AOP對不一樣類型的組件進行更多自動加強
B.再來看看IOC相關的一些註解:@Autowired自動裝配不用多說了;@Required用於在setter方法標記屬性值須要由Spring進行裝配,對於目前版本的Spring這個註解已經廢棄,如今Spring更推薦使用構造方法注入;@Qualifier用於經過給Bean定義修飾語來注入相應的Bean,和@Autowired一塊兒使用至關於@Resource的效果,固然還有一種常見用法是嵌入其它註解用於對Bean進行區分,而後配合@Autowired一塊兒使用,參見後面提到的Spring Cloud的@LoadBalanced註解;@Value用於注入屬性配置或SpEL表達式(前者是咱們常見用法,後者能夠從其它對象獲取值,功能更強大一點);@Lookup能夠實現方法注入,若是咱們的類是單例的,可是又但願Spring注入的依賴的對象是Prototype生命週期(每次new一個出來)的,這個時候能夠經過此註解進行方法注入
C. 而後來看一下有關事務的幾個註解:@EnableTransactionManagement用於開啓事務管理,使用Spring Boot若是引入Spring Data的話不須要手動開啓(不過建議你們在使用事務的時候仍是經過日誌來驗證事務管理是否生效);@Transactional你們都知道用於開啓事務以及設置傳播性、隔離性、回滾條件等;@TransactionalEventListener用於配置事務的回調方法,能夠在事務提交前、提交後、完成後以及回滾後幾個階段接受回調事件。
D. @Order註解能夠設置Spring管理對象的加載順序,在以前介紹AOP的文章中咱們看到有的時候咱們必須經過設置合理的@Order來合理安排切面的切入順序避免一些問題,還有在一些業務場景中,咱們每每會去定義一組相似於Filter的@Component,而後會從容器得到一組Bean,這個時候業務組件的運行順序每每會比較重要,也能夠經過這個方式進行排序
E. @AliasFor註解能夠設置一組註解屬性相互做爲別名,對於有歧義的時候會使代碼更清晰,此外還有一個用途是建立複合註解,Spring MVC的@GetMapping註解就是基於@RequestMapping這個註解建立的複合註解,咱們能夠很方便得經過這種方式來實現註解的繼承
Spring上下文註解
A. 首先來看一下配置相關的一些註解:@Configuration用於標註配置類,啓用Java配置方式的Bean配置;@Bean用於配置一個Bean;@ComponentScan(@ComponentScans用於配置一組@ComponentScan,Java 8能夠直接使用重複註解特性配置多個@ComponentScan)用於掃描包方式配置Bean;@PropertySource以及 @PropertySources用於導入配置文件;@Conditional用於設置關聯的條件類,在合適的時候啓用Bean的配置(Spring Boot自動配置根基);@Import用於導入其它配置類; @ImportResource用於導入非Java配置方式的XML配置;@Profile用於指定在合適的Profile下啓用配置;@Lazy用於告知容器延遲到使用的時候實例化Bean(默認狀況下容器啓動的時候實例化Bean來檢查全部的問題);@Description用於給Bean設置描述;@Scope用於設置Bean的生命週期;@Primary用於在定義了多個Bean的時候指定首選的Bean
B. 其它一些註解包括:@EventListener用於設置回調方法監聽Spring制定的以及自定義的各類事件;@EnableAspectJAutoProxy用於開啓支持AspectJ的 @Aspect切面配置支持,使用Spring Boot引入了AOP啓動器的話不須要顯式開啓
Spring Web註解
Spring MVC的各類註解對應了Spring MVC各方面的功能,下面咱們來了解一下:
A. 首先是三個定義了Bean特殊生命週期的複合註解:@RequestScope、@SessionScope和 @ApplicationScope。在Web應用中,咱們可能須要Bean跟隨請求、會話和應用程序的聲明週期來進行建立,這個時候能夠直接使用這三個快捷的複合註解
B. 接下去能夠看到各類 @XXXMapping的註解,分別用於配置HandlerMethod匹配到不一樣的Http Method,固然不使用這些快捷的註解也是能夠的,直接使用@RequestMapping而後手動設置method
C. @ResponseStatus能夠用到方法上也能夠用到異常上,前者會直接使請求獲得指定的響應代碼或緣由(能夠配合@ExceptionHandler使用),後者能夠實現遇到指定異常的時候給出指定的響應代碼或緣由,@ResponseBody咱們實現Restful接口的時候(@RestController)最經常使用了,把返回內容(序列化後)輸出到請求體
D. Spring MVC給了咱們各類註解方便咱們從HTTP請求各類地方獲取參數,@RequestBody從請求體(處理複雜數據,好比JSON),@RequestHeader從請求頭,@CookieValue從cookie中,@SessionAttribute從會話中,@RequestAttribute從請求的Attribute中(好比過濾器和攔截器手動設置的一些臨時數據),@RequestParam從請求參數(處理簡單數據,鍵值對),@PathVariable從路徑片斷,@MatrixAttribute矩陣變量容許咱們採用特殊的規則在URL路徑後加參數(分號區分不一樣參數,逗號爲參數增長多個值)
E. @ControllerAdvice是一個重要註解,容許咱們在集中的地方配置控制器(有@RequestMapping的方法)相關的加強(@RestControllerAdvice也是差很少的,只是至關於爲@ExceptionHandler加上了@ResponseBody)。那麼能夠應用哪些加強呢?首先是能夠用 @ExceptionHandler進行統一的全局異常處理;第二是 @InitBinder用來設置WebDataBinder,WebDataBinder用來自動綁定前臺請求參數到Model中;第三是 @ModelAttribute讓全局的@RequestMapping都能得到在此處設置的鍵值對。固然,這裏說的@InitBinder和@ExceptionHandler也能夠不定義在@ControllerAdvice內部(做爲全局開啓),定義在Controller內部應用到某個Controller也是能夠的
F. 其它還有一些註解好比:@CrossOrigin能夠用到Controller或Method上(須要配合@RequestMapping)設置細粒度的跨域行爲
在以前的文章中咱們也提到,對於Spring MVC,定義本身的註解應用到參數、方法、控制器上,配合HandlerMethodArgumentResolver、XXAdvise、以及Interceptor實現具體的功能來使用太太常見了,幾乎全部的非業務橫切關注點,咱們都不該該在方法實現中重複任何一行代碼。
Spring Boot註解
A. 來看一下上下文相關的註解:@ConfigurationProperties很經常使用(配合 @EnableConfigurationProperties註解來設置須要啓用的配置類),用來自定義配置類和配置文件進行關聯;@DeprecatedConfigurationProperty用於標記廢棄的配置以及設置替代配置和告知廢棄緣由;@ConfigurationPropertiesBinding用於指定自定義的轉換器用於配置解析的時的類型轉換; @NestedConfigurationProperty用於關聯外部的類型做爲嵌套配置類
B. 再看看自動配置相關的註解,自動配置是Spring Boot最重要的特性,在以前的系列文章中我有提到一個觀點,IOC是好事情,可是把組件內部的一些默認配置以及組件和組件的組裝交給外部用戶來配置實際上是不合理的,組件應當能夠自動進行自我配置實現開箱急用,只有須要自定義組件的時候纔要求外部來進行個性化配置:@EnableAutoConfiguration註解能夠啓用自動配置,Spring Boot應用程序通常咱們會直接使用複合註解@SpringBootApplication;@AutoConfigureOrder(值越小優先級越高)、@AutoConfigureAfter、@AutoConfigureBefore用於設置自動配置類加載順序,以及精確控制加載依賴關係,有的時候咱們的自動配置須要相互依賴或者會相互干擾,須要手動調節
C. 最後來看一下十幾種配置條件,用好這些註解是實現完善的自動配置的關鍵:@ConditionalOnBean用於僅當容器中已經包含指定的Bean類型或名稱時才匹配條件;@ConditionalOnClass僅當classpath上存在指定類時條件匹配;@ConditionalOnCloudPlatform僅當指定的雲平臺處於活動狀態時條件匹配;@ConditionalOnExpression依賴於SpEL表達式的值的條件元素的配置註解;@ConditionalOnJava基於應用運行的JVM版本的條件匹配;@ConditionalOnJndi基於JNDI可用和能夠查找指定位置的條件匹配;@ConditionalOnMissingBean僅當容器中不包含指定的Bean類型或名稱時條件匹配;@ConditionalOnMissingClass僅當classpath上不存在指定類時條件匹配;@ConditionalOnNotWebApplication 僅當不是WebApplicationContext(非Web項目)時條件匹配,對應 @ConditionalOnWebApplication;@ConditionalOnProperty是檢查指定的屬性是否具備指定的值;@ConditionalOnResource表示僅當 classpath 上存在指定資源時條件匹配;@ConditionalOnSingleCandidate僅當容器中包含指定的Bean類而且能夠判斷只有單個候選者時條件匹配。其實全部這些實現原理都是擴展SpringBootCondition抽象類(實現以前提到的Condition接口),咱們徹底能夠實現本身的條件註解(配合 @Conditional註解關聯到本身實現的SpringBootCondition)
Spring Cloud註解
在介紹本系列文章的第一篇中咱們就提到了,Spring Cloud整齊劃一經過各類EnableXXX註解開啓某個功能,這裏就不對這些註解進行說明了,使用Spring Boot組件的功能很是簡單,基本就是引POM+EnableXXX+設置配置文件三部曲。
A. 首先是 Netflix包下的一些註解,各類EnableXXX就不說了,參考前一篇文章,以前沒介紹過 @RibbonClient,這個註解用來爲負載均衡客戶端作一些自定義的配置,能夠進一步配置或自定義從哪裏獲取服務端列表、負載均衡策略、Ping也就是服務鑑活策略等等
B. client包下的 @SpringCloudApplication以前文章中咱們也沒有使用到,這是一個複合註解就是 @SpringBootApplication+ @EnableDiscoveryClient+ @EnableCircuitBreaker,Spring Cloud那堆東西不少,仍是本身親手定義一個一個功能的註解來的踏實; @LoadBalanced註解用於和RestTemplate配合使用構成一個負載均衡的Http客戶端,實現原理上其實這個註解是一個@Qualifier註解,Spring會爲全部@LoadBalanced的RestTemplate加入一個LoadBalancerInterceptor(實現ClientHttpRequestInterceptor)實現負載均衡
C. sleuth包下面的註解和鏈路跟蹤相關,比較經常使用的是經過 @SpanName手動設置span的名稱,其它註解對於業務開發並不經常使用
總結
好了,寫了本文我發現我看到@已經Markdown的**就眼花,請點贊支持。本文咱們經過代碼打印出了大部分Spring相關的註解,你也能夠經過這個方式熟悉其它框架的註解(畢竟註解是框架賦予咱們各類便捷功能的一個重要入口,對註解瞭解個八九成也每每能夠對框架賦予咱們的豐富功能瞭解六七成)。而後咱們梳理了一下Spring相關的各類註解,其中主要須要關注的是幾方面:
咱們知道註解其實只是一個標識,註解如何起做用背後的實現原理仍是比較多樣的,你能夠進一步結合本文介紹的Spring的各類註解探尋一下背後實現的原理。