1. 程式人生 > >品Spring:註解之王@Configuration和它的一眾“小弟們”

品Spring:註解之王@Configuration和它的一眾“小弟們”

其實對Spring的瞭解達到一定程度後,你就會發現,無論是使用Spring框架開發的應用,還是Spring框架本身的開發都是圍繞著註解構建起來的。

空口無憑,那就說個最普通的例子吧。

在Spring中要啟用一項XXX功能,標準做法就是用@EnableXXX這種“啟用”型別的註解。

那麼這種型別的註解一般都做了什麼呢?分析一下吧。

看過本號文章的人都知道,Spring的核心就是bean定義和bean,如果我想增加某方面的功能,只需寫若干個類,並作為bean定義註冊到容器中即可。

因此“啟用”型別的註解表面上看起來是啟用某項功能,背後其實就是註冊了一系列相關的bean定義到容器中。


大家都非常熟悉的,啟用事務管理功能的註解,@EnableTransactionManagement,如下圖01:


還有啟用定時任務的註解,@EnableScheduling,如下圖02:


還有啟動非同步支援的註解,@EnableAsync,如下圖03:


這類註解其實很多,就先看這三個吧。

相信大家已經發現了一個規律,這三個註解在定義時都使用@Import註解引入了某個類,最終目的當然是為了註冊bean定義。

之前的文章中已經說過多遍,這種方法叫做通過程式設計的方式(或稱寫程式碼的方式)註冊bean定義。

這種方式非常的靈活,因為@Import註解引入的類其實是兩個著名介面的實現類。


這兩個介面,之前也提到過很多次了,就是:

ImportSelector

ImportBeanDefinitionRegistrar

在實現介面時,可以按照自己關注的因素,來自主可控的(或稱有選擇性的)註冊bean定義,這就是靈活性的原因。

@Import除了可以引入這兩個介面外,還可以引入被@Configuration註解標註的類或沒有被註解標註的普通類。

再次贅述一下這種方式的典型用法:

用於Spring框架自身的開發,以及第三方框架與Spring的整合開發。

熟悉Spring的朋友都應該知道,@EnableXXX註解和@Import註解都必須(直接或間接)和@Configuration註解放到一起才會起作用。

這是Spring規定的,主要是和Spring的原始碼實現有關。

所以整體就是,@Configuration類引入了@EnableXXX註解或@Import註解,@EnableXXX註解又引入了@Import註解。

而@Import註解又引入了那兩個介面的實現類以程式設計方式註冊bean定義,這裡註冊的bean定義同樣還可以是@Configuration類。

@Import註解還可以再次引入@Configuration類。因此,我們成功的從@Configuration類出發後又回到了@Configuration類。

然後可以繼續重複這個動作,一直進行下去。

這就像原子彈爆炸的鏈式反應一樣,從最初的一個引爆點,然後開啟逐個自我引爆的模式,越來越大,一發不可收拾。

所以Spring就是採用這種模式,最初的引爆點必須是一個@Configuration類,自我的引爆模式是@Import註解。

從最初的那一個基點,向容器中引入新的類,再以引入的新類作為基點,再次引入新的類,這種爆炸模型,非常強大。

除了@Import可以引入新類外,@ComponentScan註解也以掃描jar包的方式引入新的類。只不過掃描的方式通常用於使用者程式而非框架開發。

可見Spring程式就是圍繞著註解構建起來的,從最初的一個@Configuration類,配合使用@Import和@ComponentScan,逐漸壯大。

所以最初的這一個@Configuration類,是在應用啟動前,顯式的(或稱特意的)註冊到容器中的。

SpringBoot就是非常標準的這個樣子的。它的主類就是一個@Configuration類,然後再使用@Import和@ComponentScan註解。

看看下面的註解原始碼,確實是這樣子的,如下圖0405:


終於可以下結論了:

@Configuration就是註解之王,是老大,其餘的註解圍繞在它周圍,是小弟們。

按照江湖規矩,老大得罩著小弟們,所以負責處理@Configuration註解的類也要負責處理其它的註解。

程式設計新說注:究竟有哪些註解可以和@Configuration一起使用,在上一篇文章中已寫明。


目前看來,主要是這樣的:

在容器啟動前使用程式設計的方式可以註冊bean定義,如SpringBoot的主類。

在容器啟動後,@Import和@ComponentScan都可以註冊bean定義,@Bean方法也可以。

雖然它們功能相似,但側重點其實是不同的,當然可以放到一起使用,但必須做到心中有數。

如果使用不當的話,可能會發生“重疊”,如一個類即被引入了,也被掃描了。

而且還可能被引入多次,掃描多次,當然,即使這樣也不一定報錯,因為Spring做了處理。


Spring採用了類似先入為主的做法:

首先是容器啟動前通過程式設計方式註冊的類。這些類在後面無論是被掃描還是被引入,都不會再被註冊。

其次是如果先通過掃描方式註冊的類,引入時不再註冊。如果先通過引入方式註冊的類,掃描時不再註冊。

最後是如果先通過掃描方式註冊的類,再次掃描時不再註冊。如果先通過引入方式註冊的類,再次引入時不再註冊。

這就像冪等處理一樣,給上層使用者留有較寬的餘地,在最終使用時沒有那麼嚴格。多了一些隨意。

程式設計新說注:在顯式註冊bean定義時,如果beanName已存在,會丟擲異常。


如果這些註解在一起出現的話,註冊bean定義也是有先後順序的:

第一,容器啟動前,註冊的@Configuration類的bean定義。

程式設計新說注:下面的這些內容都是依附於@Configuration類的。

第二,@ComponentScan註解掃描的bean定義。

第三,使用ImportSelector介面引入的bean定義(非ImportBeanDefinitionRegistrar介面的實現類)。

第四,使用@Import引入的普通類的bean定義。

第五,類的@Bean方法引入的bean定義。

第六,使用@ImportResource註解引入的XML檔案裡的bean定義。

第七,使用ImportBeanDefinitionRegistrar介面引入的bean定義。

第八,@Configuration類實現的接口裡的@Bean方法(預設方法)。

第九,@Configuration類的父類

接著就是把父類當作新的@Configuration類,再次重複第二到第九。

整個bean定義的註冊過程,就是以容器為依託,一遍一遍的執行,直到容器中的bean定義再無增多為止。

下面就描述其中的一遍:

第一,從容器中拿出尚未處理過的所有@Configuration類

第二,從這一批@Configuration類中解析出所有需要被註冊的類

第三,將這一批@Configuration類標記為已處理過

第四,將剛剛解析出來的類進行bean定義註冊,這些類裡面可能還含有@Configuration類。

接下來進行迴圈執行即可。直到某一次執行完後,容器中的bean定義數量沒有變化,表明已全部處理完畢。


再說一個外部類和內部類的事情,頗有意思:

@Configuration
class Outter {

    @Configuration
    static class Inner {

    }
}


先來看兩個問題:

當外部類上有註解時,分別使用@ComponentScan和@Import作用於外部類,此時註冊的bean定義是誰?

當把外部類上的註解去掉時,分別使用@ComponentScan和@Import作用於外部類,此時註冊的bean定義是誰?

來看看答案吧:

當外部類有註解時,@ComponentScan和@Import都會把Outter和Inner註冊bean定義。

當外部類沒有註解時,@ComponentScan會註冊Inner類,@Import會註冊Outter類。

下面來解釋下:

JVM規範規定,每個位元組碼檔案只能包含一個類,因此巢狀類會單獨編譯成一個位元組碼檔案。與它的外部類是互相獨立的。

而@ComponentScan註解是按位元組碼檔案掃描的,所以此時內部類都會被註冊,即使外部類上沒有註解。

當@Import引入一個類時,不管該類上是否有註解,該類本身一定會被註冊bean定義。所以外部類總是被註冊。

但是,只有外部類上有註解時,才會去處理它的內部類。且內部類上必須也要有註解才行。因為原始碼就是這樣寫的。


最後看看SpringBoot應用註冊的前幾個bean定義,如下圖06:


可見第一個註冊的bean定義,就是用來處理標有@Configuration註解的類的。

第六個註冊的是主類的bean定義。

>>> 熱門文章集錦 <<<

 

畢業10年,我有話說

【面試】我是如何面試別人List相關知識的,深度有點長文

我是如何在畢業不久只用1年就升為開發組長的

爸爸又給Spring MVC生了個弟弟叫Spring WebFlux

【面試】我是如何在面試別人Spring事務時“套路”對方的

【面試】Spring事務面試考點吐血整理(建議珍藏)

【面試】我是如何在面試別人Redis相關知識時“軟懟”他的

【面試】吃透了這些Redis知識點,面試官一定覺得你很NB(乾貨 | 建議珍藏)

【面試】如果你這樣回答“什麼是執行緒安全”,面試官都會對你刮目相看(建議珍藏)

【面試】迄今為止把同步/非同步/阻塞/非阻塞/BIO/NIO/AIO講的這麼清楚的好文章(快快珍藏)

【面試】一篇文章幫你徹底搞清楚“I/O多路複用”和“非同步I/O”的前世今生(深度好文,建議珍藏)

【面試】如果把執行緒當作一個人來對待,所有問題都瞬間明白了

Java多執行緒通關———基礎知識挑戰

品Spring:帝國的基石

 

>>> 品Spring系列文章 <<<

 

品Spring:帝國的基石

品Spring:bean定義上梁山

品Spring:實現bean定義時採用的“先進生產力”

品Spring:註解終於“成功上位”

品Spring:能工巧匠們對註解的“加持”

品Spring:SpringBoot和Spring到底有沒有本質的不同?

品Spring:負責bean定義註冊的兩個“排頭兵”

品Spring:SpringBoot輕鬆取勝bean定義註冊的“第一階段”

品Spring:SpringBoot發起bean定義註冊的“二次攻堅戰”

 

 

作者是工作超過10年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號和知識星球的二維碼,歡迎關注!

 

       

相關推薦

Spring註解@Configuration小弟

其實對Spring的瞭解達到一定程度後,你就會發現,無論是使用Spring框架開發的應用,還是Spring框架本身的開發都是圍繞著註解構建起來的。空口無憑,那就說個最普通的例子吧。在Spring中要啟用一項XXX功能,標準做法就是用@EnableXXX這種“啟用”型別的註解。那麼這種型

Spring對@PostConstruct@PreDestroy註解的處理方法

在bean的例項化過程中,也會用到一系列的相關注解。如@PostConstruct和@PreDestroy用來標記初始化和銷燬方法。平常更多的是側重於應用,很少會有人去了解它背後發生的事情。今天就來看下它們的原始碼,這樣它們對你來說就不再是黑盒子了,而且學習原始碼對每個技術人來說都是必經之路。人們對事物的認知

Spring對@Autowired@Value註解的處理方法

在Spring中能夠完成依賴注入的註解有JavaSE提供的@Resource註解,就是上一篇文章介紹的。還有JavaEE提供的@javax.inject.Inject註解,這個用的很少,因為一般都不會去引用JavaEE的jar包。程式設計新說注:JavaEE早已經被Oracle拋棄了。JavaEE這個名字已經

spring註解自動裝配屬性賦值

${} throws 定義類 on() wired 成對 功能 inter setter 1.自動裝配 1.1什麽是自動裝配?   Spring利用依賴註入(DI),完成對IOC容器中各個組件的依賴關系賦值; [email protected]/@Quali

Spring能工巧匠註解的“加持”

問題的描述與方案的提出在Spring從XML轉向註解時,為了自身的開發方便,對註解含義進行了擴充(具體參考本號上一篇文章)。這個擴充直接導致了一個問題,就是需要從註解往元註解以及元元註解(即沿著從下向上的方向)裡傳遞資料。為了更好的描述這個問題,請再看個示例:@interface A {&

SpringSpringBootSpring到底有沒有本質的不同?

現在的Spring相關開發都是基於SpringBoot的。最後在打包時可以把所有依賴的jar包都打進去,構成一個獨立的可執行的jar包。如下圖13: 使用java -jar命令就可以執行這個獨立的jar包。如下圖14: 這個jar包的執行入口就是一個main函式,典型的格式如下: @Spri

Spring對@Resource註解的處理方法

@Resource是Java的註解,表示一個資源,它具有雙向的含義,一個是從外部獲取一個資源,一個是向外部提供一個資源。這其實就對應於Spring的注入和註冊。當它用在欄位和方法上時,表示前者。當它用在類上時表示後者。Spring只提供了對前者的支援。該註解本身表示的是資源,資源的含義是很寬泛的。由於絕大部分

9. SpringAOP增強

9. Spring:AOP之增強 Spring AOP的增強型別 Spring AOP的前置增強 通過程式碼實現增強 在Spring當中,僅支援方法級別的增強,利用MethodBeforeAdvice實現,表示在目標方法執行前實施增強,步驟如下

Spring註解@Transactional在oraclmysql差異

嘿嘿 !從今天開始圖圖開始寫部落格啦 這是圖圖的第一個部落格 ,不足之處請大家指出,共同進步。 @Transactional是Spring中的事務註解,其引數為readOnly用於設定當前事務是否為只讀事務,設定為true表示只讀,false則表示可讀寫,如@Transactional(re

Spring Json View異常處理錯誤處理

Spring Json View之錯誤處理和異常處理  錯誤處理   錯誤處理是在控制器增加一些公共或欄位級別的錯誤(繫結結果集)到Model後觸發的。   配置   HttpStatusError   ModelFlagError   自主實現JsonErro

Spring原始碼分析BeanPostProcessor介面BeanFactoryPostProcessor介面方法不執行原因分析

首先下面是我的Bean /* * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License

學習 Spring (十) 註解 @Bean, @ImportResource, @Value

ner .data mys oot xtend ace data con 方法 Spring入門篇 學習筆記 @Bean @Bean 標識一個用於配置和初始化一個由 Spring IoC 容器管理的新對象的方法,類似於 XML 配置文件的 可以在 Spring 的 @

Spring帝國的基石

序生活是一杯酒,有時需要麻醉自己,才能夠暫時忘卻痛苦與不快。生活是一杯茶,有時需要細細品味,才發現苦澀背後也會有甘甜。Spring是一杯酒,一眼望不到邊的官方文件,著實讓人難以下嚥。Spring是一杯茶,在無邊的原始碼中暢遊之後,發現色相味道俱全。高考狀元是六月份的網紅,Spring帝國是Java界的明星。狀

Springbean定義上梁山

認真閱讀,收穫滿滿,向智慧又邁進一步。。。技術不枯燥,先來點閒聊先說點好事高興一下。前段時間看新聞說,我國正式的空間站建設已在進行當中。下半年,長征五號B運載火箭將在海南文昌航天發射場擇機將空間站核心艙發射升空。預計用2到3年將空間站建好。雖然到時你們不讓我上去,不過我也為這件事出不了什麼力,算扯平了。哈哈,

Spring實現bean定義時採用的“先進生產力”

前景回顧當我們把寫好的業務程式碼交給Spring之後,Spring都會做些什麼呢?仔細想象一下,再稍微抽象一下,Spring所做的幾乎全部都是:“bean的例項化,bean的依賴裝配,bean的初始化,bean的方法呼叫,bean的銷燬回收”。那問題來了,Spring為什麼能夠準確無誤

Spring負責bean定義註冊的兩個“排頭兵”

別看Spring現在玩的這麼花,其實它的“籌碼”就兩個,“容器”和“bean定義”。只有先把bean定義註冊到容器裡,後續的一切可能才有可能成為可能。所以在進階的路上如果要想走的順暢些,徹底搞清楚bean定義註冊的所有細節至關重要。畢竟這

SpringSpringBoot輕鬆取勝bean定義註冊的“第一階段”

上一篇文章強調了bean定義註冊佔Spring應用的半壁江山。而且詳細介紹了兩個重量級的註冊bean定義的類。今天就以SpringBoot為例,來看看整個SpringBoot應用的bean定義是如何註冊進容器的。先來看看經典的啟動入口,如下圖01: 可以看到呼叫的是run方法,並把主類(main或pr

SpringSpringBoot發起bean定義註冊的“二次攻堅戰”

上一篇文章整體非常輕鬆,因為在容器啟動前,只註冊了一個bean定義,就是SpringBoot的主類。OK,今天接著從容器的啟動入手,找出剩餘所有的bean定義的註冊過程。具體細節肯定會頗為複雜,同樣,大家只需關注都幹了什麼,不用考慮如何幹的。來巨集觀的看下容器的啟動過程,即refresh方法,如下圖01:

Springbean工廠後處理器的呼叫規則

上一篇文章介紹了對@Configuration類的處理邏輯,這些邏輯都寫在ConfigurationClassPostProcessor類中。 這個類不僅是一個“bean工廠後處理器”,還是一個“bean定義註冊後處理器”。這其實是兩個介面,它們都是來操作be

Spring詳細解說bean後處理器

一個小小的里程碑首先感謝能看到本文的朋友,感謝你的一路陪伴。如果每篇都認真看的話,會發現本系列以bean定義作為切入點,先是詳細解說了什麼是bean定義,接著又強調了bean定義為什麼如此重要。然後又講了獲取bean定義詳細資訊的方法,接著又講了bean定義註冊的若干種方式,然後是bean定義註冊方式的實現細