別看Spring現在玩的這麼花,其實它的“籌碼”就兩個,“容器”和“bean定義”。只有先把bean定義註冊到容器里,後續的一切可能才有可能成為可能。所以在進階的路上如果要想走的順暢些,徹底搞清楚bean定義註冊的所有細節至關重要。畢竟這是萬里長征的第一步。有句話怎麼說來著,“勿在浮沙築高臺”。Sp ...
別看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定義的方式不同。
下麵看第二個