1. 程式人生 > >品Spring:負責bean定義註冊的兩個“排頭兵”

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

別看Spring現在玩的這麼花,其實它的“籌碼”就兩個,“容器”和“bean定義”。

只有先把bean定義註冊到容器裡,後續的一切可能才有可能成為可能。

所以在進階的路上如果要想走的順暢些,徹底搞清楚bean定義註冊的所有細節至關重要。

畢竟這是萬里長征的第一步。有句話怎麼說來著,“勿在浮沙築高臺”。


Spring步入註解和Java配置的時代也有些時日了。而且也旗幟鮮明的表達了bean的註冊方法。

這不,就是這個介面,AnnotationConfigRegistry,如下圖01:


再來看下這個介面的名字,有三個單片語成,Annotation、Config、Registry。


第一個表示註解,第二個表示Java配置,第三個表示註冊。

合起來的意思可以理解為,基於註解和Java配置的bean定義註冊。當然,這是我猜的,哈哈哈。

這是一個很牛X的介面,理由見下圖02:

 

納尼,所有的容器類都實現了它。

不管是web的、非web的,傳統Spring的、SpringBoot的,響應式的、Servlet的。

簡直是老少通吃、婦孺皆宜啊。淡定、淡定。

這個介面的兩個方法非常簡約:

一個是直接把一個類(Class<?>)進行註冊。

一個是通過掃描指定的包(Package)裡的類進行註冊。

請注意我剛剛使用了“簡約”而沒有使用“簡單”,因為簡約往往並不等於簡單,反而更多時候等於難。



講了這麼多,終於可以讓今天的主角登場了,來來來,掌聲響起來。

就是這兩個類:

AnnotatedBeanDefinitionReader

ClassPathBeanDefinitionScanner

第一個類就是站在介面第一個方法register背後默默付出的。

第二個類就是負責搞定介面第二個方法scan後面所有事情的。

下面開始進行具體的講解,只需要知道都幹了什麼即可,至於怎麼幹的,不需要了解。

看第一個類,如下圖03:

 

第一個欄位型別是BeanDefinitionRegistry,這是容器(或bean factory)會實現的介面。

用於把一個BeanDefinition(bean定義)註冊到容器中,看下它的這個方法,如下圖04:


 

程式設計新說注:對“bean定義”這個概念不清楚的,可以在文末檢視本系列《品Spring》文章的頭幾篇。

剛剛應該看到在註冊bean定義時需要一個bean名稱(即beanName),因此該第二個欄位發揮作用了。

它就是BeanNameGenerator。例如,有一個類是UserController,它上面標了註解@Controller("user")。

首先它會把註解的value屬性作為名稱,此時就是user啦。

如果沒有指定value屬性,就像這樣@Controller,此時就是類的短名稱且首字母小寫,即userController。

這就是bean名稱的生成策略,在實際開發中不就是這樣的嘛。

第三個欄位是ScopeMetadataResolver,是來決定bean例項的範圍(即生命週期)的。

常見的生命週期有四種,PROTOTYPE(原型)、SINGLETON(單例)、REQUEST(請求)、SESSION(會話)。

就是通過檢查類上有沒有@Scope這個註解。如果有的話,就按指定的走,沒有的話,就按單例走。

第四個欄位是ConditionEvaluator,條件計算器,根據“條件”判斷一個bean定義該不該被註冊。

這可是SpringBoot自動配置(AutoConfiguration)的基石啊。

就是去檢測類上有沒有標@Conditional這個註解。如果沒有的話,bean定義會被註冊。

如果有的話,需要再去計算具體的“條件”,然後才能確定bean定義到底要不要註冊。

哎呀,註冊一個bean定義好麻煩啊,喘口氣,繼續吧。嘿嘿。


下面開始真正進入註冊的方法,先看下方法的引數吧,如下圖05:

 

 

方法共有5個引數,只有第一個是必須的,後面的都可以為空。

第一個引數,annotatedClass,是Class<?>,表示要被註冊的類。

第二個引數,instanceSupplier,是一個函式式介面,Supplier<T>,可以提供這個bean的例項物件,這樣就不再需要通過反射呼叫構造函數了。

第三個引數,name,bean名稱,如果傳的話就不用再生成了。

第四個引數,qualifiers,是一組用作限定修飾符的註解,Class<? extends Annotation>[]。

第五個引數,definitionCustomizers,是一組可以自定義bean定義的介面,BeanDefinitionCustomizer。

整個處理過程分為九步,如下圖06:

 

第一步,先把類轉變為bean定義,即把Class<?>轉變為BeanDefinition。具體是AnnotatedGenericBeanDefinition這個類。

第二步,使用條件計算器來確定是否要註冊這個bean定義。

第三步,確定這個bean的生命週期。

第四步,確定這個bean的名稱。

第五步,處理定義的公共註解資訊。如下圖07:

 

就是@Lazy、@Primary、@DependsOn、@Role、@Description這五個註解。

從類上分別獲取這些註解,然後從註解中讀出需要的資訊,再把這些資訊設定到bean定義中。

第六步,處理限定修飾符,就是@Primary、@Lazy、@Qualifier這三個註解。

這個幾個註解是從方法引數傳入的,上一步的註解是從類上讀取的,它們不重複也不衝突。

程式設計新說注:這些註解的含義和用法,這裡就不說了,畢竟這是“追求深度”的文章。

第七步,應用bean定義自定義器,對bean定義進行一些自定義。

第八步,根據bean的生命週期,使用AOP技術為該bean定義生成代理。

第九步,把這個bean定義註冊到容器中。

這就是一個bean定義的完整註冊過程。媽呀,讓我歇會兒。

程式設計新說注:第二個類註冊bean定義的整體邏輯和第一個類完全一樣。只是獲取bean定義的方式不同。

下面看第二個類,如下圖08:

 

首先可以看到,它掃描的都是jar包中的.class檔案。

然後還有兩個過濾器集合,決定哪些被排除、哪些被包含。

當然,類中也給出了預設情況下包含的,如下圖09:

 

第一,@Component註解以及用它定義的其它註解,如@Configuration等。

第二,JSR-250裡面的@javax.annotation.ManagedBean註解。

第三,JSR-330裡面的@javax.inject.Named註解。

標了這三個註解的類都會被註冊,第一個註解是Spring的,後兩個是Java的。

預設情況下,排除過濾器沒有指定,也就是不進行任何顯式的排除。

具體收集bean定義的過程,分為七步,如下圖10:

 

第一步,拼接資源路徑,形式就是這樣classpath*:org/cnt/ts/**/*.class。

它表示搜尋類路徑下所有的jar包裡,以org/cnt/ts開頭的包及其子包裡的所有.class檔案。

第二步,找出上一步中的那些.class檔案,並把它們轉化為資源,即Resource類。

第三步,使用ASM框架逐個讀取這些資源(其實就是位元組碼檔案啦)。

第四步,應用過濾器和條件計算器,來確定這個bean定義是否要被註冊。

如下圖11:

 

第五步,使用從位元組碼中讀出的內容來構建BeanDefinition,使用的是ScannedGenericBeanDefinition這個類。

第六步,確認下這個類是否符合要求,如下圖12:

 

一共有三項檢查:

第一,必須是獨立的。可以是頂級類(非內部類),可以是靜態內部類(即static class)。

第二,必須是具體的,即非抽象的。

第三,如果類是抽象的,它必須包含一個標有@Lookup註解的方法,來指定一個具體的bean。

第七步,收集好這個bean定義。

這些bean定義抽取好後,剩下的處理就和第一個類一樣了。

如下圖13:

 

 

也是確定生命週期,生成bean名稱,處理定義的公共註解資訊,根據生命週期生成代理,最後註冊到容器中。

最後宣告一點:

以上兩個類並不處理@Bean這個註解註冊的bean定義,也不處理由@Import註解引入的bean定義。

哪誰處理呢?後續文章見。

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

 

品Spring:帝國的基石

品Spring:bean定義上梁山

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

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

相關推薦

Spring負責bean定義註冊排頭兵

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

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

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

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

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

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

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

Springbean定義上梁山

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

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

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

Spring詳細解說bean後處理器

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

Spring真沒想到,三十步才能完成一個bean例項的建立

在容器啟動快完成時,會把所有的單例bean進行例項化,也可以叫做預先例項化。這樣做的好處之一是,可以及早地發現問題,及早的丟擲異常,及早地解決掉。本文就來看下整個的例項化過程。其實還是比較繁瑣的。一、從容器中找出所有的bean定義名稱因為不知道誰是單例bean,所以只能先全部找出來。如下圖01: 二、

Spring(五)之Bean定義繼承和依賴註入

dia 繼承 map() path 形式 集合 .text inf ima 一、Bean定義繼承 bean定義可以包含許多配置信息,包括構造函數參數,屬性值和特定於容器的信息,例如初始化方法,靜態工廠方法名稱等。 子bean定義從父定義繼承配置數據。子定義可以根據需要覆蓋某

Spring註解實現Bean定義

12.3.1  概述 前邊介紹的Bean定義全是基於XML方式定義配置元資料,且在【12.2註解實現Bean依賴注入】一節中介紹了通過註解來減少配置數量,但並沒有完全消除在XML配置檔案中的Bean定義,因此有沒有方式完全消除XML配置Bean定義呢?

Spring帝國的基石

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

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

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

SpringSpringBoot和Spring到底有沒有本質的不同?

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

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

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

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

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

Spring對@Resource註解的處理方法

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

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

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

Spring關於@Scheduled定時任務的思考與探索,結果尷尬了

非Spring風格的程式碼與Spring的結合現在的開發都是基於Spring的,所有的依賴都有Spring管理,這沒有問題。但是要突然寫一些非Spring風格的程式碼時,可能會很不習慣,如果還要和Spring風格的程式碼結合起來的話,就會稍顯麻煩。因為非Spring風格的程式碼不由Spring管理,所以Spr

Unity (一) AnimatorCurves : 曲線運用的案例

組件 過程 以及 nim 角色 技術 span png -1 案例1: 在角色死亡動畫中,角色倒地,然而碰撞體並沒有進行調整,如果對此角色位置進行射擊,還會有碰撞檢測,為了避免這種問題,可以運用曲線Curves將角色的碰撞體進行調整 Curves:曲線

詳盡分析世紀之戰360VS騰訊是階層的抗爭

廣告 策略 路線 出了 之一 推出 黑名單 放下 反思 很不錯的一篇文字 分析的也很透徹 【轉自中國移動http://labs.chinamobile.com/】 來源:搜狐IT 作者:吃熊掌的魚 2010-11-01 10:11:51 [ 13967閱讀