1. 程式人生 > >品Spring:真沒想到,三十步才能完成一個bean例項的建立

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

在容器啟動快完成時,會把所有的單例bean進行例項化,也可以叫做預先例項化。

這樣做的好處之一是,可以及早地發現問題,及早的丟擲異常,及早地解決掉。

本文就來看下整個的例項化過程。其實還是比較繁瑣的。

一、從容器中找出所有的bean定義名稱

因為不知道誰是單例bean,所以只能先全部找出來。如下圖01:


二、迴圈遍歷所有的bean名稱,檢查是否符合條件

首先要合併bean定義,因為bean定義可以有父子關係,類似繼承。

然後這個合併後的bean定義必須是,非抽象的,單例的,非延遲初始化的。

那麼它就滿足條件,如下圖02:


三、判斷是否為FactoryBean<?>型別

如果不是的話,說明該beanName對應一個普通的bean,可以直接例項化。


如果是的話,說明該beanName對應的是一個工廠,這個工廠本身是單例的。

但是它裡面生產的bean不一定是單例的。即使是的話,還要判斷是否要積極的去初始化工廠裡的bean。

具體的判斷如下圖03:


程式設計新說注:提到FactoryBean<?>型別,是否想起&符號的作用呢?

四、開始進入眾所熟知的getBean(String name)方法

在上一圖中可以看到Spring對bean的例項化時竟然是呼叫的getBean(..)方法。

這樣共用一套程式碼,簡單省事。不僅如此,當獲取一個bean的依賴時,也可以用該方法。

這樣getBean(..)就是一個綜合方法,沒有bean例項就生成,有的話就直接返回。


如下圖04:


五、對手工直接註冊的單例物件進行檢測

bean例項除了可以用bean定義生成外,還可以由開發人員直接註冊一個bean例項。

這樣在使用bean定義生成例項前,先使用beanName去手動註冊的bean例項集合中找一下。

如下圖05:


如果找到了,就不用生成了,否則就會根據bean定義生成bean例項。

六、對FactoryBean<?>型別的檢測

這和上面提到的是一個型別,它是一個工廠,可以認為是包裹在了實際bean例項的外面。

這樣可以有一些特殊的作用,不好之處就是每次都要檢測下,然後從它內部拿出實際的bean例項。

具體檢測過程不再展開,如下圖06:


七、對型別進行轉換,如果有必要的話


上面我們僅僅是用beanName去手動註冊的例項集合中尋找,萬一這個手動註冊的例項型別和bean定義要求的不相容呢?

因此要進行型別檢測與轉換,實在不行就拋異常,如下圖07:


如果成功的話,就表明手動註冊的bean定義例項滿足要求,將它返回即可。

程式設計新說注:如果在第五步沒有找到beanName對應的手動註冊的bean例項,那開始根據bean定義來生成bean例項。繼續往下看。

八、準備好顯式指定的依賴,如@DependsOn指定的

先獲取合併後的bean定義,然後從中讀出顯式指定的依賴,並逐個處理。

使用registerDependentBean(..)將依賴關係寫入容器,由容器維護。

並同樣使用getBean(..)方法例項化這些依賴,一模一樣的套路。

其實就是遞迴,如下圖08:


程式設計新說注:接下來使用createBean(..)方法正式開始建立bean

九、解析出bean的Class<?>

因為在註冊bean定義時並不一定載入類,可能只是一個字串的類名稱。

所以要根據類名稱去載入類,並得到類的Class<?>。如下圖09:


十、呼叫bean後處理器的postProcessBeforeInstantiation方法

此時還處在例項化之前,讓使用者有機會來提供一個bean例項或代理。

這樣Spring就不再進行後續的例項化步驟,直接返回這個使用者提供的。

如果使用者沒有提供的話,Spring繼續後續的處理。如下圖1011:


十一、呼叫InstanceSupplier生成bean例項,如果有的話

在註冊bean定義時,可以設定一個Supplier<?>型別的函式式介面。

其實就是使用者可以提供一段建立bean例項的程式碼,這樣Spring就使用它來建立bean例項。

然後將這個例項返回即可,如下圖12:


十二、通過FactoryMethod來生成bean例項,如果FactoryMethodName不為null的話

如下圖13:


FactoryMethod就是工廠方法,說明bean的例項是通過呼叫這個工廠方法返回的,而不是通過反射呼叫建構函式返回的。

工廠方法有兩種,靜態的和例項的。如果是例項的,那還要有一個FactoryBeanName來指定一個bean名稱,根據它可以從容器中獲取一個物件,用作工廠。

如果是靜態的,那就不需要例項了,直接把bean定義中的型別作為工廠類即可。如下圖14:


然後根據工廠方法的名稱,從bean定義中解析出對應的Method物件。然後再解析出構造方法引數用作工廠方法的引數。

最終通過反射呼叫這個工廠方法,獲取返回值,就是bean例項了,如下圖15:


這個bean例項會用一個BeanWrapper介面進行包裝,這個介面提供一些基礎的JavaBean功能,如資料的型別轉換然後再進行屬性繫結等。

十三、呼叫bean後處理器的determineCandidateConstructors方法來確定候選構造方法

如下圖16:


這裡涉及到從多個候選構造方法中選出一個最合適的,是一個比較複雜的過程。

最後也是通過反射呼叫構造方法,獲取到bean的例項。如下圖17:


然後也用BeanWrapper介面進行包裝。

十四、使用更適合的構造方法來例項化,如果有的話

如果上一步沒有執行的話,則使用bean定義中更適合的構造方法,如下圖18:


十五、使用預設無參的構造方法來例項化

如果上一步沒有執行的話,則使用預設無參構造方法,如下圖19:


程式設計新說注:至此bean例項已經建立好了。

十六、應用bean後處理器的postProcessMergedBeanDefinition方法

上兩篇文章詳細介紹了bean後處理器,主要是用來實現註解的功能的。

如下圖2021:


十七、此時就可以暴露早期的bean引用了,如果需要的話

如允許迴圈引用的話,就需要這個操作,如下圖22:


十八、應用bean後處理器的postProcessAfterInstantiation方法

如下圖23:


且該方法如果返回false,該bean例項後續的bean後處理器操作將不再執行。

十九、根據設定的自動裝配型別處理自動裝配問題

如下圖24:


如果配置的是按名稱自動裝配,則會把所有setter方法中引數型別是非基本型別的都找出來。

然後按照屬性名稱從容器中找出同名的bean,作為屬性值儲存起來以備後用。如下圖25:


如果配置的是按型別自動裝配,則會把所有setter方法中引數型別是非基本型別的都找出來。

然後按照屬性型別從容器中解析出對應的bean,作為屬性值儲存起來以備後用。如下圖26:


如果看了上兩篇文章,會發現這裡按型別從容器中解析bean的套路和上兩篇一模一樣。

程式設計新說注:

以上的setter方法上不需要標任何註解,因為顯式設定了自動裝配型別。

而預設情況其實是沒有設定的,即AUTOWIRE_NO,所以我們要標上@Autowired註解。

二十、應用bean後處理器的postProcessProperties方法

在這一步其實是完成了依賴的注入,如下圖27:


二十一、其餘屬性值到bean屬性的繫結

這一步是由BeanWrapper這個介面完成的,如下圖28:


程式設計新說注:至此bean的所有依賴裝配和屬性設定都已完畢。

二十二、應用bean後處理器的postProcessBeforeInitialization方法

這一步就開始執行初始化方法了。如下圖2930:


二十三、執行bean的初始化方法afterPropertiesSet()

如果bean實現了InitializingBean介面,此刻會呼叫它唯一的方法。

如下圖31:


二十四、執行bean定義中指定的初始化方法initMethod

如果bean定義是使用@Bean註冊的,可以通過設定註解屬性指定初始化方法。

如下圖32:


程式設計新說注:

之前文章中寫過,有三種方式可以指定初始化方法:

1)@PostConstruct註解,2)InitializingBean介面,3)@Bean註解

這裡有兩個問題需要記住:

1)如果兩種或三種方式都指向了同一個方法,這個方法也只會被執行一次。

2)三種方式指定的初始化方法的執行順序就按剛剛列出的1、2、3這個順序。

二十五、應用bean後處理器的postProcessAfterInitialization方法

如下圖3334:

相關推薦

Spring想到才能完成一個bean例項建立

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

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

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

Spring Boot 基礎系列教程 | 第二篇使用@Async實現非同步呼叫自定義執行緒池

推薦 Spring Boot/Cloud 視訊: 在之前的Spring Boot基礎教程系列中,已經通過《Spring Boot中使用@Async實現非同步呼叫》一文介紹過如何使用@Async註解來實現非同步呼叫了。但是,對於這些非同步執行的控制是我們保障自身

有一天派大星走了。海綿寶寶說

有一塊海綿,他叫做海綿寶寶。 他很好,每天臉上都掛著大大的微笑_ 他很好,他的世界裡從沒有悲傷_ 他很好,能做出好吃的蟹黃堡_ 他很好,不會委屈,不會嘲笑_ 他很好,永遠有一個朋友陪他笑陪他玩_ 有一塊海星,他叫做派大星。 他很傻,卻能給人安全感。 他很

Spring MVC學習筆記3種對映方式得到Handler

這篇部落格寫的是通過三種方式對映得到Handler 第一種:配置HandlerMapping,根據beanname進行查詢: 配置如下: <form action="hello.do">         hello:<input type="text"

演算法題目判斷陣列中任意條邊能否組成一個三角形若能返回1不能返回0

演算法一:正確率90%,演算法效率分0% 演算法二:正確率,演算法效率均100% public class Triangle { public static void main(String

Android Studio中的控制元件初始化疑問build問題但執行模擬器時出現問題。錯誤程式碼java.lang.RuntimeException: Unable to instant

在使用Android Studio,啟動模擬器時,出現如下錯誤:Logcat中的資訊如下:java.lang.RuntimeException:Unable to instantiate activityComponentInfo{com.example.dcyyw.appl

Spring帝國的基石

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

Springbean定義上梁山

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

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

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

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

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

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

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

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:

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

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

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

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

Spring詳細解說bean後處理器

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

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

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

Spring對@Resource註解的處理方法

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