位元組跳動Java三面涼涼
二、Spring生命週期的大膽猜測
這裡分享一個閱讀原始碼的小技巧:捉大放小,連蒙帶猜!
8字真言,我們在閱讀原始碼過程中,因為你要知道,每一個被開源出來的優秀框架,其原始碼的體系都是極其龐大複雜的,我們不能面面俱到,所以在看原始碼過程中一定不能被細枝末節纏住,一定要先理清楚整個框架的一個大致思想和大致的框架體系,再去搞那些細枝末節,其效率會好很多,其次在看原始碼過程中,我們一定要大膽的去想,去猜測,如果這個功能讓你自己去寫,你會怎麼實現!
我們今天學習SpringBean的生命週期也是按照這個8字真言去學習,通過我們之前所學,Spring大致有以下的功能:
- 他會幫我們自動的建立物件然後儲存起來!
- 他會幫我們完成屬性的填充!
- 如果我們設定了Aop的功能,他會幫我們自動的代理,實現切面功能!
我們從平常的使用中,至少可以得知以上的三點,如果讓你自己去實現,必會如何實現呢?
- 首先他既然能夠幫我們自己建立物件,那麼他肯定是通過反射來建立的,通過反射來建立,就必定繞不過去要使用Class物件建立,那麼我們如何獲取Class物件呢? 去掃描專案,將指定的包下的加了註解的類檔案切割獲取Class名稱,通過反射載入Class名稱,反射建立java物件!
- 我們要完成屬性的填充,為了方便和效能方面,我肯定會把這些建立好的物件儲存起來,無疑
Map
容器是最合適的! - 我們在建立一個物件完成之後,反射拿到裡面的屬性,如果需要填充,我們先去我們之前儲存的容器裡面去取,取不出來在反射吧這個依賴的屬性創建出來,然後填充進物件再儲存在容器裡面,從而完成了屬性的注入!
- 填充完成屬性之後,我們那當前物件,取與Aop邏輯進行對比,判斷是否需要代理,不需要則建立完成,儲存進Map容器,需要代理則對當前這個類進行
jdk
或者cglib
的代理然後再儲存進容器裡面!
於是乎,我們自己實現了一個Spring管理一個Bean的所有過程,畫個圖,他大概長這樣!
自己實現看起來,整個流程就很清晰,掃描、建立、注入、代理、儲存一應俱全,但是Spring的實現方式遠比我們自己實現的要複雜的多得多!
三、Spring的生命週期流程
Spring作者希望,Spring再著手管理一個Bean的時候,它希望能夠讓Spring的使用者能夠插手,Spring把一個類物件變成一個Java Object的每一步,怎麼理解呢?
比如我們買了一棟新房子,這個房子需要取裝修,你自己去裝修誠然不夠專業,不能夠面面俱到,所以是我們就找了一個裝修公司幫助我們裝修新房,於是裝修公司就開始預先畫好的圖紙進行裝修,但是在裝修的過程中,你為了讓自己的新家更加溫馨,你想掛一些壁畫在牆上,但是圖紙上卻沒有!於是你就找裝修公司,要求裝修公司在新家的牆上掛上一些壁畫!裝修公司在接受到你的請求之後,就吩咐裝修的工人在圖紙之外去給你在牆上掛上壁畫之後,然後再接著裝修!
上面這個小故事有 這樣幾個角色,我們把它和Spring對照起來!
- 你:代表框架的使用者!
- 新房:代表一個Class檔案,你自己也能夠裝修,但是不夠專業,所以交給裝修公司! 那麼你自己建立物件可能某些使用用起來很麻煩,所以我們交給了Spring容器!
- 裝修公司:代表著Spring容器!
- 圖紙:代表預設步驟,Spring原本就存在的步驟!
- 工人:Spring提供的各種介面!我們可以通過Spring工廠提供的介面做各種自定義的配置!
上面的小故事,大致可以描述Spring生命週期的核心思想!Spring再對一個Class檔案例項化成具體的Spring Bean的時候,它提供了各種介面,由我們自己實現!然後再例項化過程中,不同的時機,去呼叫不同的介面!從而完成Spring的整個生命週期的建立!
Spring的生命週期大致分為以下部分!
-
掃描專案,將專案指定目錄下的Class檔案轉換為Class物件!
-
讀取Class物件屬性包裝為
BeanDefinition
,然後儲存再一個Map中!(不難理解,他是為了後續建立或者讀取這個類的資訊更加方便取而創立的) -
將全部的類轉化為
BeanDefinition
並儲存之後,開始呼叫第一個回撥介面BeanFactoryPostProcessor#postProcessBeanFactory()
!- 它的呼叫時機是將掃描到的Class檔案轉換為
BeanDefinition
之後呼叫的,我們可以通過回撥的方法獲取所有的BeanDefinition
,而後續的所有對Class的操作都是基於BeanDefinition
操作的,所以,我們可以通過修改它,來改變後續的流程!
- 它的呼叫時機是將掃描到的Class檔案轉換為
-
先從當前的容器物件取當前要建立的物件,當取出來的物件為null時開始著手建立物件!
-
做一系列的驗證,比如驗證這個類是否被排除、是否正在建立中、是否有依賴Bean【@DependsOn】註解、是否時單例等等!
-
驗證通過之後,開始通過反射建立這個物件!
-
合併
BeanDefinition
,這裡涉及到Spring之前版本使用的父子容器的概念,屬於另外一個知識點不做講解! -
判斷當前物件是不是單例、是不是支援迴圈引用、是不是正在建立等!
-
執行第二個介面回撥
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
方法!- 它的執行時機時例項化完成之後,屬性填充之前,它的返回值是一個布林值,當返回false時,不做自動屬性填充!
-
執行第三個介面回撥
InstantiationAwareBeanPostProcessor#postProcessProperties()
方法!- 他的執行時機是,例項化之後,屬性填充檢查之後,屬性填充之前!它會返回一個屬性,後續的屬性填充會使用這個方法返回的值!我們可以在這個方法裡面修改對應Bean的注入的值!
-
填充屬性到物件!
-
呼叫第四個回撥介面
BeanNameAware#setBeanName()
方法!- 呼叫時機:屬性填充給完畢後,呼叫初始化方法之前;它的功能是能獲取bean的Name!
-
呼叫第五個回撥介面
BeanClassLoaderAware#setBeanClassLoader()
- 呼叫時機:
BeanNameAware
之後,他的功能是傳入bean的類載入器;
- 呼叫時機:
-
呼叫第六個回撥介面
BeanFactoryAware#setBeanFactory()
!- 呼叫時機:
BeanClassLoaderAware
之後,用於設定beanFactory!
- 呼叫時機:
-
呼叫第七個回撥介面
BeanPostProcessor#postProcessBeforeInitialization()
方法- 呼叫時機是部分
Aware
之後,初始化方法之前!傳入當前例項化好的物件和beanName,再初始化前做修改!
- 呼叫時機是部分
-
回撥第八個比較重要的生命週期的初始化方法,它可以是一個
InitializingBean
介面的bean,也可以是xml中配置的類,也可以是被加了@PostConstruct
註解的方法!- 該方法內部邏輯可以使用者自己編寫,呼叫時機為:例項化完成之後呼叫!
-
回撥第九個回撥介面
BeanPostProcessor#postProcessAfterInitialization()
方法!- 該方法的呼叫時機為初始化方法執行之後,這裡也是Bean例項化後的最後一步,也是SpringAop實現的重要的一步!
-
註冊銷燬方法,以便Spring容器銷燬的時候進行方法的銷燬!
整體的方法流程示例圖如下:
四、對應原始碼結構圖
最後
每年轉戰網際網路行業的人很多,說白了也是衝著高薪去的,不管你是即將步入這個行業還是想轉行,學習是必不可少的。作為一個Java開發,學習成了日常生活的一部分,不學習你就會被這個行業淘汰,這也是這個行業殘酷的現實。
如果你對Java感興趣,想要轉行改變自己,那就要趁著機遇行動起來。或許,這份限量版的Java零基礎寶典能夠對你有所幫助。
領取這份Java零基礎寶典,只需要點選這裡即可免費下載