天天看點

面試官問我SpringBean生命周期,我

面試官:今天要不來聊聊Spring對Bean的生命周期管理?

候選者:嗯,沒問題的。

候選者:很早之前我就看過源碼,但Spring源碼的實作類都太長了

候選者:我也記不得很清楚某些實作類的名字,要不我大概來說下流程?

面試官:沒事,你開始吧

候選者:首先要知道的是

候選者:普通Java對象和Spring所管理的Bean執行個體化的過程是有些差別的

候選者:在普通Java環境下建立對象簡要的步驟可以分為:

候選者:1):java源碼被編譯為被編譯為class檔案

候選者:2):等到類需要被初始化時(比如說new、反射等)

候選者:3):class檔案被虛拟機通過類加載器加載到JVM

候選者:4):初始化對象供我們使用

候選者:簡單來說,可以了解為它是用Class對象作為「模闆」進而建立出具體的執行個體

面試官:嗯…

候選者:而Spring所管理的Bean不同的是,除了Class對象之外,還會使用BeanDefinition的執行個體來描述對象的資訊

候選者:比如說,我們可以在Spring所管理的Bean有一系列的描述:@Scope、@Lazy、@DependsOn等等

候選者:可以了解為:Class隻描述了類的資訊,而BeanDefinition描述了對象的資訊

面試官:嗯,這我大緻了解你的意思了。

面試官:你就是想告訴我,Spring有BeanDefinition來存儲着我們日常給Spring Bean定義的中繼資料(@Scope、@Lazy、@DependsOn等等),對吧?

候選者:不愧是你

面試官:趕緊的,繼續吧

候選者:Spring在啟動的時候需要「掃描」在XML/注解/JavaConfig 中需要被Spring管理的Bean資訊

候選者:随後,會将這些資訊封裝成BeanDefinition,最後會把這些資訊放到一個beanDefinitionMap中

候選者:我記得這個Map的key應該是beanName,value則是BeanDefinition對象

候選者:到這裡其實就是把定義的中繼資料加載起來,目前真實對象還沒執行個體化

候選者:接着會周遊這個beanDefinitionMap,執行BeanFactoryPostProcessor這個Bean工廠後置處理器的邏輯

候選者:比如說,我們平時定義的占位符資訊,就是通過BeanFactoryPostProcessor的子類PropertyPlaceholderConfigurer進行注入進去

候選者:當然了,這裡我們也可以自定義BeanFactoryPostProcessor來對我們定義好的Bean中繼資料進行擷取或者修改

候選者:隻是一般我們不會這樣幹,實際上也很有少的使用場景

候選者:BeanFactoryPostProcessor後置處理器執行完了以後,就到了執行個體化對象啦

候選者:在Spring裡邊是通過反射來實作的,一般情況下會通過反射選擇合适的構造器來把對象執行個體化

候選者:但這裡把對象執行個體化,隻是把對象給建立出來,而對象具體的屬性是還沒注入的。

候選者:比如我的對象是UserService,而UserService對象依賴着SendService對象,這時候的SendService還是null的

候選者:是以,下一步就是把對象的相關屬性給注入(:

候選者:相關屬性注入完之後,往下接着就是初始化的工作了

候選者:首先判斷該Bean是否實作了Aware相關的接口,如果存在則填充相關的資源

候選者:比如我這邊在項目用到的:我希望通過代碼程式的方式去擷取指定的Spring Bean

候選者:我們這邊會抽取成一個工具類,去實作ApplicationContextAware接口,來擷取ApplicationContext對象進而擷取Spring Bean

候選者:Aware相關的接口處理完之後,就會到BeanPostProcessor後置處理器啦

候選者:BeanPostProcessor後置處理器有兩個方法,一個是before,一個是after(那肯定是before先執行、after後執行)

候選者:這個BeanPostProcessor後置處理器是AOP實作的關鍵(關鍵子類AnnotationAwareAspectJAutoProxyCreator)

候選者:是以,執行完Aware相關的接口就會執行BeanPostProcessor相關子類的before方法

候選者:BeanPostProcessor相關子類的before方法執行完,則執行init相關的方法,比如說@PostConstruct、實作了InitializingBean接口、定義的init-method方法

候選者:當時我還去官網去看他們的被調用「執行順序」分别是:@PostConstruct、實作了InitializingBean接口以及init-method方法

候選者:這些都是Spring給我們的「擴充」,像@PostConstruct我就經常用到

候選者:比如說:對象執行個體化後,我要做些初始化的相關工作或者就啟個線程去Kafka拉取資料

候選者:等到init方法執行完之後,就會執行BeanPostProcessor的after方法

候選者:基本重要的流程已經走完了,我們就可以擷取到對象去使用了

候選者:銷毀的時候就看有沒有配置相關的destroy方法,執行就完事了

面試官:嗯,了解,但我的觀衆好像不太滿意,總感覺少了些什麼。

面試官:你看過Spring是怎麼解決循環依賴的嗎?

面試官:如果現在有個A對象,它的屬性是B對象,而B對象的屬性也是A對象

面試官:說白了就是A依賴B,而B又依賴A,Spring是怎麼做的?

候選者:嗯,這塊我也是看過的,其實也是在Spring的生命周期裡面嘛

候選者:從上面我們可以知道,對象屬性的注入在對象執行個體化之後的嘛。

候選者:它的大緻過程是這樣的:

候選者:首先A對象執行個體化,然後對屬性進行注入,發現依賴B對象

候選者:B對象此時還沒建立出來,是以轉頭去執行個體化B對象

候選者:B對象執行個體化之後,發現需要依賴A對象,那A對象已經執行個體化了嘛,是以B對象最終能完成建立

候選者:B對象傳回到A對象的屬性注入的方法上,A對象最終完成建立

候選者:這是大緻的過程;

面試官:聽起來你還會原理哦?

候選者:Absolutely

候選者:至于原理,其實就是用到了三級的緩存

候選者:所謂的三級緩存其實就是三個Map…首先明确一定,我對這裡的三級緩存定義是這樣的:

候選者:singletonObjects(一級,日常實際擷取Bean的地方);

候選者:earlySingletonObjects(二級,還沒進行屬性注入,由三級緩存放進來);

候選者:singletonFactories(三級,Value是一個對象工廠);

候選者:再回到剛才講述的過程中,A對象執行個體化之後,屬性注入之前,其實會把A對象放入三級緩存中

候選者:key是BeanName,Value是ObjectFactory

候選者:等到A對象屬性注入時,發現依賴B,又去執行個體化B時

候選者:B屬性注入需要去擷取A對象,這裡就是從三級緩存裡拿出ObjectFactory,從ObjectFactory得到對應的Bean(就是對象A)

候選者:把三級緩存的A記錄給幹掉,然後放到二級緩存中

候選者:顯然,二級緩存存儲的key是BeanName,value就是Bean(這裡的Bean還沒做完屬性注入相關的工作)

候選者:等到完全初始化之後,就會把二級緩存給remove掉,塞到一級緩存中

候選者:我們自己去getBean的時候,實際上拿到的是一級緩存的

候選者:大緻的過程就是這樣

面試官:那我想問一下,為什麼是三級緩存?

候選者:首先從第三級緩存說起(就是key是BeanName,Value為ObjectFactory)

候選者:我們的對象是單例的,有可能A對象依賴的B對象是有AOP的(B對象需要代理)

候選者:假設沒有第三級緩存,隻有第二級緩存(Value存對象,而不是工廠對象)

候選者:那如果有AOP的情況下,豈不是在存入第二級緩存之前都需要先去做AOP代理?這不合适嘛

候選者:這裡肯定是需要考慮代理的情況的,比如A對象是一個被AOP增量的對象,B依賴A時,得到的A肯定是代理對象的

候選者:是以,三級緩存的Value是ObjectFactory,可以從裡邊拿到代理對象

候選者:而二級緩存存在的必要就是為了性能,從三級緩存的工廠裡建立出對象,再扔到二級緩存(這樣就不用每次都要從工廠裡拿)

候選者:應該很好懂吧?

面試官:确實(:

候選者:我稍微總結一下今天的内容吧

候選者:怕你的觀衆說不滿意,那我就沒有贊了,沒有贊我就很難受

候選者:首先是Spring Bean的生命周期過程,Spring使用BeanDefinition來裝載着我們給Bean定義的中繼資料

候選者:執行個體化Bean的時候實際上就是周遊BeanDefinitionMap

候選者:Spring的Bean執行個體化和屬性指派是分開兩步來做的

候選者:在Spring Bean的生命周期,Spring預留了很多的hook給我們去擴充

候選者:1):Bean執行個體化之前有BeanFactoryPostProcessor

候選者:2):Bean執行個體化之後,初始化時,有相關的Aware接口供我們去拿到Context相關資訊

候選者:3):環繞着初始化階段,有BeanPostProcessor(AOP的關鍵)

候選者:4):在初始化階段,有各種的init方法供我們去自定義

候選者:而循環依賴的解決主要通過三級的緩存

候選者:在執行個體化後,會把自己扔到三級緩存(此時的key是BeanName,Value是ObjectFactory)

候選者:在注入屬性時,發現需要依賴B,也會走B的執行個體化過程,B屬性注入依賴A,從三級緩存找到A

候選者:删掉三級緩存,放到二級緩存

面試官:嗯,你要不後面放點關鍵的源碼吧

候選者:這你倒是提醒我了,确實有必要

面試官:這要是能聽懂,是真的看過源碼才行(:還好我看過

關鍵源碼方法(強烈建議自己去撸一遍)

<code>org.springframework.context.support.AbstractApplicationContext#refresh</code>(入口)

<code>org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization</code>(初始化單例對象入口)

<code>org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons</code>(初始化單例對象入口)

<code>org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)</code>(萬惡之源,擷取并建立Bean的入口)

<code>org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean</code>(實際的擷取并建立Bean的實作)

<code>org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)</code>(從緩存中嘗試擷取)

<code>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])</code>(執行個體化Bean)

<code>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean</code>(執行個體化Bean具體實作)

<code>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance</code>(具體執行個體化過程)

<code>org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory</code>(将執行個體化後的Bean添加到三級緩存)

<code>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean</code>(執行個體化後屬性注入)

<code>org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)</code>(初始化入口)

面試官問我SpringBean生命周期,我
面試官問我SpringBean生命周期,我
面試官問我SpringBean生命周期,我