新聞中心
在跟學(xué)弟學(xué)妹們聊完Spring IOC之后,有學(xué)弟反饋他們面試經(jīng)常會(huì)遇到面試官詢問Spring 里面的循環(huán)依賴問題。

創(chuàng)新互聯(lián)建站是專業(yè)的梁山網(wǎng)站建設(shè)公司,梁山接單;提供成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行梁山網(wǎng)站開發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
而作為面試者的他們來(lái)說(shuō)就只能答出用三層緩存處理,而不清楚為什么是三層緩存?;谝陨蠁栴}還是再跟學(xué)弟學(xué)妹們分析一下Spring中的循環(huán)依賴問題。
什么是循環(huán)依賴?
- 假設(shè)現(xiàn)在有一個(gè)對(duì)象A里面有一個(gè)屬性Class B,同樣的Class B對(duì)象中有一個(gè)Class A 的對(duì)象屬性,那么這兩個(gè)對(duì)象能相互創(chuàng)建成功嗎?
可能一般的普通代碼來(lái)說(shuō)肯定是可以實(shí)現(xiàn):
- A a = new A()
- B b = new B()
- a.setB(b)
- b.setA (a)
看過之前講的IOC的同學(xué)應(yīng)該知道Spring官方是推薦使用構(gòu)造器注入的,所以如果是通過構(gòu)造器注入那就會(huì)產(chǎn)生一個(gè)無(wú)限循環(huán)注入的問題了,如下圖所示,永遠(yuǎn)出來(lái)不?
- A a = new A( new B( new A(new B(......))))
所以面試過程中的循環(huán)依賴問題其實(shí)都是問Setter方式內(nèi)部如何解決循環(huán)依賴的?而不是問的構(gòu)造器。
比較初級(jí)的回答可能會(huì)說(shuō) 是通過三層緩存,再好一點(diǎn)的回加上 三層緩存加上 提前暴露對(duì)象的方式(半成品)解決循環(huán)依賴問題
那什么是提前暴露對(duì)象呢?說(shuō)白了就是spring IOC 容器的啟動(dòng)過程 bean 的整個(gè)生命周期過程處理的邏輯。之前跟大家聊SpringIOC的過程已經(jīng)跟大家詳細(xì)分享過了,就不再啰嗦了,還不了解的可以再去復(fù)習(xí)一下。
這里就直接再畫一個(gè)流程圖,大家針對(duì)這個(gè)圖做一下回歸復(fù)習(xí):
上面的這張圖其實(shí)就是給大家說(shuō)明了我們創(chuàng)建對(duì)象的時(shí)候可以分為兩個(gè)大步驟,一個(gè)實(shí)例化,一個(gè)初始化。
同樣的現(xiàn)在接著回到上面的問題,Setter是在哪一步處理緩存依賴的呢?
回顧整個(gè)流程我們大致可以按照這個(gè)思路來(lái):
一個(gè)對(duì)象的創(chuàng)建 -> 實(shí)例化 -> 初始化(設(shè)置屬性值)
那構(gòu)造器的那種方式在流程中怎么體現(xiàn)出這個(gè)環(huán)呢?給大家畫了一個(gè)圖如下:
- springIOC容器中的bean默認(rèn)都是單例的,這個(gè)大家應(yīng)該清楚的。所以在設(shè)置屬性的時(shí)候可以直接在容器中獲取,按照上面的創(chuàng)建流程那整個(gè)循環(huán)依賴就產(chǎn)生了。
- 三層緩存依賴,其實(shí)就是先把實(shí)例化的對(duì)象,放置在緩存中,等后續(xù)在根據(jù)A對(duì)象的引用完成賦值操作。
處理完的流程就是如下所示了:
在改進(jìn)的圖中其實(shí)已經(jīng)可以發(fā)現(xiàn),環(huán) 已經(jīng)被打開了。整個(gè)可以如下幾步:
在實(shí)例化A對(duì)象之后就向容器中添加一個(gè)緩存,存放一個(gè)實(shí)例化但未初始化完成的對(duì)象(半成品對(duì)象)。
在第一次創(chuàng)建A對(duì)象中容器已經(jīng)有一個(gè)A對(duì)象,但是沒有B對(duì)象,所以在開始創(chuàng)建B對(duì)象時(shí),在完成B對(duì)象的實(shí)例化之后,開始初始化屬性賦值時(shí),此時(shí)容器中已經(jīng)有A對(duì)象,所以可以直接通過A的屬性賦值,同樣的B對(duì)象完成初始化之后也就可以再接著完成初始化A對(duì)象了,那整個(gè)A對(duì)象和B對(duì)象的創(chuàng)建過程就完成了。
廢話不多說(shuō)了還是直接看下Spring中源碼來(lái)解析一下:
- * @author Juergen Hoeller
- * @since 2.0
- * @see #registerSingleton
- * @see #registerDisposableBean
- * @see org.springframework.beans.factory.DisposableBean
- * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
- */
- ublic class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
- /** Cache of singleton objects: bean name to bean instance. */
- private final Map
singletonObjects = new ConcurrentHashMap<>(256); - /** Cache of singleton factories: bean name to ObjectFactory. */
- private final Map
> singletonFactories = new HashMap<>(16); - /** Cache of early singleton objects: bean name to bean instance. */
- private final Map
earlySingletonObjects = new HashMap<>(16); - // 省略其他的一些方法。。。
- 一級(jí)緩存:singletonObjects
- 二級(jí)緩存:earlySingletonObjects
- 三級(jí)緩存:singletonFactories,第三級(jí)緩存存放的是ObjectFactory-》FunctionalInterface 即函數(shù)式接口
那么Spring中是怎么使用這三級(jí)緩存去處理依賴呢?
為了搞明白這個(gè)過程只能是debug源碼了,因?yàn)檎麄€(gè)過程比較長(zhǎng),沒辦法做成動(dòng)圖的形式,所以只能給大家一步一步說(shuō)明了。
之前跟大家講SpringIOC中的有個(gè)關(guān)鍵方法refresh(),這里面包含了13個(gè)核心的子方法,不了解的同學(xué)可以去復(fù)習(xí)一下前面講的SpringIOC啟動(dòng)過程。
在13個(gè)子方法中有一個(gè)finishBeanFactoryInitialization(beanFactory) ;初始化剩下的單實(shí)例(非懶加載的)方法。這個(gè)就是開始入口了。
- public void refresh() throws BeansException, IllegalStateException {
- // 添加一個(gè)synchronized 防止出現(xiàn)refresh還沒有完成出現(xiàn)其他的操作(啟動(dòng),或者銷毀)
- synchronized (this.startupShutdownMonitor) {
- // 1.準(zhǔn)備工作
- // 記錄下容器的啟動(dòng)時(shí)間、
- // 標(biāo)記“已啟動(dòng)”狀態(tài),關(guān)閉狀態(tài)為false、
- // 加載當(dāng)前系統(tǒng)屬性到環(huán)境對(duì)象中
- // 準(zhǔn)備一系列監(jiān)聽器以及事件集合對(duì)象
- prepareRefresh();
- // 2. 創(chuàng)建容器對(duì)象:DefaultListableBeanFactory,加載XML配置文件的屬性到當(dāng)前的工廠中(默認(rèn)用命名空間來(lái)解析),就是上面說(shuō)的BeanDefinition(bean的定義信息)這里還沒有初始化,只是配置信息都提取出來(lái)了,(包含里面的value值其實(shí)都只是占位符)
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // 3. BeanFactory的準(zhǔn)備工作,設(shè)置BeanFactory的類加載器,添加幾個(gè)BeanPostProcessor,手動(dòng)注冊(cè)幾個(gè)特殊的bean等
- prepareBeanFactory(beanFactory);
- try {
- // 4.子類的覆蓋方法做額外的處理,就是我們剛開始說(shuō)的 BeanFactoryPostProcessor ,具體的子類可以在這步的時(shí)候添加一些特殊的BeanFactoryPostProcessor完成對(duì)beanFactory修改或者擴(kuò)展。
- // 到這里的時(shí)候,所有的Bean都加載、注冊(cè)完成了,但是都還沒有初始化
- postProcessBeanFactory(beanFactory);
- // 5.調(diào)用 BeanFactoryPostProcessor 各個(gè)實(shí)現(xiàn)類的 postProcessBeanFactory(factory) 方法
- invokeBeanFactoryPostProcessors(beanFactory);
- // 6.注冊(cè) BeanPostProcessor 處理器 這里只是注冊(cè)功能,真正的調(diào)用的是getBean方法
- registerBeanPostProcessors(beanFactory);
- // 7.初始化當(dāng)前 ApplicationContext 的 MessageSource,即國(guó)際化處理
- initMessageSource();
- // 8.初始化當(dāng)前 ApplicationContext 的事件廣播器,
- initApplicationEventMulticaster();
- // 9.從方法名就可以知道,典型的模板方法(鉤子方法),感興趣的同學(xué)還可以再去復(fù)習(xí)一下之前寫的設(shè)計(jì)模式中的-模版方法模式
- // 具體的子類可以在這里初始化一些特殊的Bean(在初始化 singleton beans 之前)
- onRefresh();
- // 10.注冊(cè)事件監(jiān)聽器,監(jiān)聽器需要實(shí)現(xiàn) ApplicationListener 接口。這也不是我們的重點(diǎn),過
- registerListeners();
- // 11.初始化所有的 singleton beans(lazy-init 的除外),重點(diǎn)關(guān)注
- finishBeanFactoryInitialization(beanFactory);
- // 12.廣播事件,ApplicationContext 初始化完成
- finishRefresh();
- }
- catch (BeansException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Exception encountered during context initialization - " +
- "cancelling refresh attempt: " + ex);
- }
- // 13.銷毀已經(jīng)初始化的 singleton 的 Beans,以免有些 bean 會(huì)一直占用資源
- destroyBeans();
- cancelRefresh(ex);
- // 把異常往外拋
- throw ex;
- }
- finally {
- // Reset common introspection caches in Spring's core, since we
- // might not ever need metadata for singleton beans anymore...
- resetCommonCaches();
- }
- }
- }
1.因?yàn)镮OC作為Spring的容器,且默認(rèn)的都是單例的,所以在我們創(chuàng)建bean之前都會(huì)去getBean一把,判斷當(dāng)前是否有,當(dāng)沒有時(shí)才會(huì)去創(chuàng)建。
所以進(jìn)入finishBeanFactoryInitialization方法中找到 beanFactory.preInstantiateSingletons();
- protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
- // 省略其他干擾代碼(判斷邏輯)。。。
- // Instantiate all remaining (non-lazy-init) singletons.
- // 實(shí)例化剩下的所有的單例對(duì)象(非懶加載的)
- beanFactory.preInstantiateSingletons();
- }
進(jìn)入到 preInstantiateSingletons 方法中,可以看到通過beanDefinitionNames(bean的定義信息)來(lái)判斷當(dāng)前需要?jiǎng)?chuàng)建的bean信息,所以開始通過beanName循環(huán)開始走創(chuàng)建流程。
因?yàn)槭俏覀儎?chuàng)建的普通的bean實(shí)例,所以肯定會(huì)走到最下面的getBean(beanName);方法中,如下代碼所示:
- @Override
- public void preInstantiateSingletons() throws BeansException {
- if (logger.isTraceEnabled()) {
- logger.trace("Pre-instantiating singletons in " + this);
- }
- // Iterate over a copy to allow for init methods which in turn register new bean definitions.
- // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
- List
beanNames = new ArrayList<>(this.beanDefinitionNames); - // Trigger initialization of all non-lazy singleton beans...
- for (String beanName : beanNames) {
- RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
- if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
- // 判斷是否是工廠bean
- if (isFactoryBean(beanName)) {
- Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
- if (bean instanceof FactoryBean) {
- final FactoryBean> factory = (FactoryBean>) bean;
- boolean isEagerInit;
- if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
- isEagerInit = AccessController.doPrivileged((PrivilegedAction
) - ((SmartFactoryBean>) factory)::isEagerInit,
- getAccessControlContext());
- }
- else {
- isEagerInit = (factory instanceof SmartFactoryBean &&
- ((SmartFactoryBean>) factory).isEagerInit());
- }
- if (isEagerInit) {
- getBean(beanName);
- }
- }
- }
- else {
- // 如果當(dāng)前beanName對(duì)應(yīng)的bean不是工廠bean,則通過beanName來(lái)獲取bean的實(shí)例
- getBean(beanName);
- }
- }
- }
- }
進(jìn)入到這個(gè)getBean(beanName);方法中有一個(gè)doGetBean方法,在Spring源碼中真正開始干活做事情的都一定會(huì)打上do的前綴方法。
- @Override
- public Object getBean(String name) throws BeansException {
- // 實(shí)際獲取bean的方法,觸發(fā)依賴注入方法
- return doGetBean(name, null, null, false);
- }
所以在進(jìn)到doGetBean的方法中,還是會(huì)默認(rèn)先去獲取一把,沒有則開始創(chuàng)建進(jìn)入createBean(beanName, mbd, args)方法。
- protected
T doGetBean(final String name, @Nullable final Class requiredType, - @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
- final String beanName = transformedBeanName(name);
- Object bean;
- // Eagerly check singleton cache for manually registered singletons.
- // 確認(rèn)一下容器中是否已經(jīng)有了當(dāng)前bean實(shí)例
- Object sharedInstance = getSingleton(beanName);
- if (sharedInstance != null && args == null) {
- if (logger.isTraceEnabled()) {
- if (isSingletonCurrentlyInCreation(beanName)) {
- logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
- "' that is not fully initialized yet - a consequence of a circular reference");
- }
- else {
- logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
- }
- }
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
- }
- // 省略其他邏輯代碼。。。
- // Create bean instance.
- // 創(chuàng)建Bean的實(shí)例對(duì)象
- if (mbd.isSingleton()) {
- // 返回以beanName的單例對(duì)象,如果沒有注冊(cè),則使用singletonFactory創(chuàng)建并且注冊(cè)一個(gè)。
- sharedInstance = getSingleton(beanName, () -> {
- try {
- // 為給定的BeanDefinition(和參數(shù))創(chuàng)建一個(gè)Bean的實(shí)例 重點(diǎn)
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
- // 省略其他邏輯代碼。。。
- return (T) bean;
- }
在上面沒有獲取到bean時(shí)候則開始創(chuàng)建bean了,所以直接進(jìn)到createBean的方法中,因?yàn)槭侨萜鞒跏蓟瘑?dòng)所以肯定是沒有的,顧一定會(huì)進(jìn)入createBean的方法中,所以再進(jìn)入createBean的方法中。
- @Override
- protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
- throws BeanCreationException {
- // 省略其他相關(guān)代碼。。。。。
- try {
- // 實(shí)際創(chuàng)建Bean的調(diào)用 重點(diǎn)
- Object beanInstance = doCreateBean(beanName, mbdToUse, args);
- if (logger.isTraceEnabled()) {
- logger.trace("Finished creating instance of bean '" + beanName + "'");
- }
- return beanInstance;
- }
- catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
- // A previously detected exception with proper bean creation context already,
- // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
- throw ex;
- }
- catch (Throwable ex) {
- throw new BeanCreationException(
- mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
- }
- }
看到doCreateBean方法那說(shuō)明要開始真正的創(chuàng)建Bean了。
- protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
- throws BeanCreationException {
- // Instantiate the bean.
- BeanWrapper instanceWrapper = null;
- if (mbd.isSingleton()) {
- // 判斷如果是單例對(duì)象,則從factoryBean實(shí)例緩存匯總移除當(dāng)前Bean的定義信息
- instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
- }
- if (instanceWrapper == null) {
- // 根據(jù)執(zhí)行的bean使用的對(duì)應(yīng)的策略創(chuàng)建新的實(shí)例。也可以理解實(shí)例化對(duì)象,在內(nèi)存總開辟空間
- instanceWrapper = createBeanInstance(beanName, mbd, args);
- }
- final Object bean = instanceWrapper.getWrappedInstance();
- Class> beanType = instanceWrapper.getWrappedClass();
- if (beanType != NullBean.class) {
- mbd.resolvedTargetType = beanType;
- }
- // 省略其他的相關(guān)代碼。。。。。
- // Eagerly cache singletons to be able to resolve circular references
- // even when triggered by lifecycle interfaces like BeanFactoryAware.
- // 判斷當(dāng)前bean是否需要提前曝光,單例&允許循環(huán)依賴&當(dāng)前bean正在創(chuàng)建,檢測(cè)循環(huán)依賴
- boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
- isSingletonCurrentlyInCreation(beanName));
- if (earlySingletonExposure) {
- if (logger.isTraceEnabled()) {
- logger.trace("Eagerly caching bean '" + beanName +
- "' to allow for resolving potential circular references");
- }
- // 在bean的初始化完成之前將創(chuàng)建的實(shí)例加入ObjectFactory(添加三級(jí)緩存),主要是為了防止后期的循環(huán)依賴。。。。重點(diǎn)
- addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- }
- Object exposedObject = bean;
- try {
- // 填充bean屬性,假設(shè)其中存在依賴于其他的bean的屬性,則會(huì)遞歸初始化依賴的bean
- populateBean(beanName, mbd, instanceWrapper);
- //執(zhí)行初始化邏輯
- exposedObject = initializeBean(beanName, exposedObject, mbd);
- }
- catch (Throwable ex) {
- if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
- throw (BeanCreationException) ex;
- }
- else {
- throw new BeanCreationException(
- mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
- }
- }
- return exposedObject;
- }
進(jìn)入到doCreateBean中首先需要核心看的一個(gè)方法createBeanInstance,這個(gè)方法就是真正的創(chuàng)建bean實(shí)例例,也就是在內(nèi)存中開辟空間(實(shí)例化),完事之后就開始看第二個(gè)重點(diǎn)添加緩存。
- addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- 這個(gè)方法點(diǎn)進(jìn)去,其實(shí)就是能發(fā)現(xiàn)開始添加到第三級(jí)緩存中,value值就是一個(gè)函數(shù)方法getEarlyBeanReference,不熟悉的同學(xué)可以看下JDK1.8的新特性。同時(shí)也標(biāo)注了當(dāng)前bean正在注冊(cè)中。
實(shí)例化完bean按照bean的生命周期流程那肯定就是開始初始化bean了,填充屬性,接著向下看有一個(gè)populateBean(填充bean屬性)。
- populateBean(beanName, mbd, instanceWrapper);
在populateBean這個(gè)過程中就有很大的邏輯在里面了,比如說(shuō)獲取屬性名稱,屬性值等等一系列操作。但是核心的還是需要看applyPropertyValues方法屬性賦值,如下所示:
- protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
- // 省略一堆其他判斷校驗(yàn)邏輯代碼,直接看到最后。。。
- if (pvs != null) {
- // 應(yīng)用給定的屬性值,解決任何在這個(gè)bean工廠運(yùn)行時(shí)其他的bean的調(diào)用(就是設(shè)置屬性值)
- applyPropertyValues(beanName, mbd, bw, pvs);
- }
- }
同樣的進(jìn)入applyPropertyValues方法。
- protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
- // 省略其他的一些校驗(yàn)代碼。。。。。
- // Create a deep copy, resolving any references for values.
- List
deepCopy = new ArrayList<>(original.size()); - boolean resolveNecessary = false;
- // 便利屬性,將屬性轉(zhuǎn)換為對(duì)應(yīng)類的對(duì)應(yīng)屬性類型
- for (PropertyValue pv : original) {
- // 判斷當(dāng)前屬性是否已經(jīng)解析過
- if (pv.isConverted()) {
- deepCopy.add(pv);
- }
- else {
- // 獲取屬性明層
- String propertyName = pv.getName();
- // 獲取屬性值
- Object originalValue = pv.getValue();
- // valueResolver處理pv解析出的originalValue封裝的對(duì)象(是否必要開始去處理屬性值了)重點(diǎn)
- Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
- // 默認(rèn)轉(zhuǎn)換后的值等于解析出來(lái)的值
- Object convertedValue = resolvedValue;
- // 判斷轉(zhuǎn)換標(biāo)記
- boolean convertible = bw.isWritableProperty(propertyName) &&
- !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
- if (convertible) {
- convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
- }
- // 省略其他的代碼邏輯。。。。
- }
applyPropertyValues方法中需要注意的是valueResolver.resolveValueIfNecessary值處理器。
- Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
這一步主要是判斷屬性值是否需要處理,因?yàn)橹斑@個(gè)value值是存方法接口方法
所以在執(zhí)行valueResolver.resolveValueIfNecessary方法時(shí),一定會(huì)去處理,那再看看里面又處理什么邏輯?
- public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
- // We must check each value to see whether it requires a runtime reference
- // to another bean to be resolved.
- // 如果value是RuntimeBeanReference實(shí)例 則處理
- if (value instanceof RuntimeBeanReference) {
- RuntimeBeanReference ref = (RuntimeBeanReference) value;
- // 解析出對(duì)應(yīng)的ref 所封裝的Bean元信息(Bean的名稱,Bean的類型) 的對(duì)象
- return resolveReference(argName, ref);
- }
- // 省略其他的邏輯代碼
- }
面的斷點(diǎn)的截圖已經(jīng)可以明確的看到value值是RuntimeBeanReference實(shí)例,所以接下來(lái)就一定會(huì)去調(diào)用resolveReference方法解析ref所封裝的bean信息,那就再接著進(jìn)入resolveReference方法看看干了什么?
- @Nullable
- private Object resolveReference(Object argName, RuntimeBeanReference ref) {
- try {
- Object bean;
- String refName = ref.getBeanName();
- refName = String.valueOf(doEvaluate(refName));
- if (ref.isToParent()) {
- if (this.beanFactory.getParentBeanFactory() == null) {
- throw new BeanCreationException(
- this.beanDefinition.getResourceDescription(), this.beanName,
- "Can't resolve reference to bean '" + refName +
- "' in parent factory: no parent factory available");
- }
- bean = this.beanFactory.getParentBeanFactory().getBean(refName);
- }
- else {
- // 獲取resolvedName的Bean對(duì)象 重點(diǎn)
- bean = this.beanFactory.getBean(refName);
- // 注冊(cè)beanName到dependentBeanName的依賴關(guān)系到Bean的工中
- this.beanFactory.registerDependentBean(refName, this.beanName);
- }
- if (bean instanceof NullBean) {
- bean = null;
- }
- // 返回解析出來(lái)的對(duì)用的ref所封裝的Bean對(duì)象
- return bean;
- }
- catch (BeansException ex) {
- throw new BeanCreationException(
- this.beanDefinition.getResourceDescription(), this.beanName,
- "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
- }
- }
上面已經(jīng)進(jìn)入到resolveReference來(lái)處理ref中所以引用的Bean對(duì)象,又因?yàn)镾pringIOC默認(rèn)都是單例Bean,所以肯定還是在beanFactory中去獲取Bean。
- bean = this.beanFactory.getBean(refName);
至此又開始循環(huán)創(chuàng)建循環(huán)依賴的對(duì)象,假設(shè)還是一開始的A和B兩個(gè)對(duì)象來(lái)說(shuō),那么開始是創(chuàng)建A對(duì)象時(shí),在設(shè)置B屬性的時(shí)候,沒有B屬性,那么現(xiàn)在剛好就是開始創(chuàng)建B屬性了。同樣的B對(duì)象又開始填充屬性A。
細(xì)心的同學(xué)應(yīng)發(fā)現(xiàn)問題了,這不就是無(wú)限循環(huán)了嗎?還怎么處理循環(huán)啊?這不是扯淡嗎?
其實(shí)不是的,其實(shí)創(chuàng)建B對(duì)象想的時(shí)候,去獲取A的Bean信息時(shí),因?yàn)锳還是在創(chuàng)建中,所以在接下來(lái)中從新走流程中會(huì)有一個(gè)新的發(fā)現(xiàn),進(jìn)入緩存中獲取對(duì)象,如下:
- bean = this.beanFactory.getBean(refName) -> doGetBean(name, null, null, false) -> sharedInstance = getSingleton(beanName) -> getSingleton(beanName, true)
- // 具體點(diǎn) getSingleton 方法的內(nèi)部實(shí)現(xiàn)
- // 進(jìn)入getSingleton方法中 isSingletonCurrentlyInCreation 當(dāng)前的Bean正在創(chuàng)建中
- protected Object getSingleton(String beanName, boolean allowEarlyReference) {
- // 從一級(jí)緩存獲取BeanName對(duì)應(yīng)的單例對(duì)象
- Object singletonObject = this.singletonObjects.get(beanName);
- // 如果沒有獲取到,但是當(dāng)前 BeanName對(duì)應(yīng)的單例對(duì)象又處于創(chuàng)建中
- if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
- synchronized (this.singletonObjects) {
- // 從二級(jí)緩存中獲取當(dāng)前BeanName對(duì)應(yīng)的單例對(duì)象
- singletonObject = this.earlySingletonObjects.get(beanName);
- // 二級(jí)緩存中沒有,但是allowEarlyReference為true,在doCreateBean方法中已經(jīng)設(shè)置,所以這里為true
- if (singletonObject == null && allowEarlyReference) {
- // 從三級(jí)緩存中獲取
- ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
- if (singletonFactory != null) {
- // 這里就是三級(jí)緩存函數(shù)方法,同過Factory創(chuàng)建一個(gè)單例對(duì)象
- singletonObject = singletonFactory.getObject();
- // 添加到二級(jí)緩存中,半成品對(duì)象
- this.earlySingletonObjects.put(beanName, singletonObject);
- // 同時(shí)刪除三級(jí)緩存
- this.singletonFactories.remove(beanName);
- }
- }
- }
- }
- // 返回當(dāng)前半成品對(duì)象
- return singletonObject;
- }
現(xiàn)在整個(gè)流程中二級(jí)緩存已經(jīng)存放了一個(gè)半成品A的對(duì)象,因此在創(chuàng)建B對(duì)象時(shí),獲取A屬性填充值從容器緩存中已經(jīng)可以獲取到A對(duì)象的單例Bean,對(duì)B對(duì)象來(lái)說(shuō)其實(shí)就是一個(gè)完整的單例Bean實(shí)例,因此再次getSingleton Bean時(shí)候會(huì)有一個(gè)判斷,如果有一個(gè)新的完成的單例Bean則會(huì)添加到一級(jí)緩存中,源碼如下:
- public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {
- Assert.notNull(beanName, "Bean name must not be null");
- synchronized (this.singletonObjects) {
- Object singletonObject = this.singletonObjects.get(beanName);
- if (singletonObject == null) {
- if (this.singletonsCurrentlyInDestruction) {
- throw new BeanCreationNotAllowedException(beanName,
- "Singleton bean creation not allowed while singletons of this factory are in destruction " +
- "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
- }
- beforeSingletonCreation(beanName);
- boolean newSingleton = false;
- boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
- if (recordSuppressedExceptions) {
- this.suppressedExceptions = new LinkedHashSet<>();
- }
- try {
- singletonObject = singletonFactory.getObject();
- newSingleton = true;
- }
- // 省略其他的代碼邏輯
- //判斷生成了新的單例對(duì)象
- if (newSingleton) {
- // 將添加BeanName和 singletonObject 添加到一級(jí)緩存中去
- addSingleton(beanName, singletonObject);
- }
- }
- return singletonObject;
- }
- }
上面聊到當(dāng)新的單例對(duì)象生成會(huì)再調(diào)用addSingleton方法。
- protected void addSingleton(String beanName, Object singletonObject) {
- synchronized (this.singletonObjects) {
- // 添加到一級(jí)緩存中
- this.singletonObjects.put(beanName, singletonObject);
- // 移除二級(jí)緩存中的內(nèi)容
- this.singletonFactories.remove(beanName);
- // 移除三級(jí)緩存中的內(nèi)容
- this.earlySingletonObjects.remove(beanName);
- // 將完成的BeanName添加到已經(jīng)注冊(cè)的單例集合中
- this.registeredSingletons.add(beanName);
- }
- }
自此整個(gè)Spring的循環(huán)依賴過程就已經(jīng)結(jié)束了。
還是用開始的A,B兩個(gè)對(duì)象來(lái)總結(jié)一個(gè)流程吧
- 當(dāng)開始創(chuàng)建A對(duì)象時(shí),實(shí)例化后,添加一步三級(jí)緩存,針對(duì)屬性賦值,因?yàn)榇藭r(shí)還沒有B對(duì)象的實(shí)例,所以在獲取到A對(duì)象的B屬性的值的ref引用對(duì)象B,觸發(fā)創(chuàng)建B對(duì)象的創(chuàng)建,因此在B對(duì)象實(shí)例化后,在屬性賦值時(shí),獲取到A屬性的ref引用對(duì)象,而因?yàn)橹癆對(duì)象已經(jīng)完成實(shí)例化,并且添加到了三級(jí)緩存中,所以在B屬性創(chuàng)建設(shè)置A屬性時(shí),因?yàn)榇藭r(shí)A屬性正在被創(chuàng)建,所以可以從第三級(jí)緩存中獲取到值,同時(shí)把獲取到的值添加到二級(jí)緩存中,同時(shí)刪除第三級(jí)緩存的A對(duì)象。
- 在創(chuàng)建B對(duì)象中已經(jīng)能獲取到A屬性值(半成品),所以B對(duì)象可以完成賦值狀態(tài),變成一個(gè)完整的B對(duì)象的實(shí)例。所以當(dāng)新的單例對(duì)象生成會(huì)再調(diào)用addSingleton方法添加到一級(jí)緩存中,同時(shí)刪除 二級(jí) 三級(jí)緩存的值,所以回過頭來(lái)接著 A對(duì)象獲取B屬性值的時(shí)候已經(jīng)能在一級(jí)緩存中獲取到。所以也就可以完成屬性賦值,自此循環(huán)依賴完全打開。
循環(huán)依賴問題已經(jīng)跟大家聊完了,在看源碼的過程中大家一定要注意以下的6個(gè)方法:
這六個(gè)方法是核心處理流程,按照這個(gè)流程,以及我上面執(zhí)行的步驟一步一步斷點(diǎn)多走幾遍就能加深自己的理解了。
不要問我為啥知道這么多都是熬夜學(xué)習(xí)找資料肝出來(lái)的!!!
總結(jié)
還是之前的老步驟聊完之后跟大家介紹幾個(gè)比較常見的面試題來(lái)加深一個(gè)理解,也方便學(xué)弟學(xué)妹們面試。
一級(jí)二級(jí) 三級(jí)緩存中分別存放的是什么狀態(tài)的對(duì)象?
完整的看完這個(gè)文章的同學(xué)應(yīng)該是沒啥問題吧
- 一級(jí):完整的成品的對(duì)象
- 二級(jí):非完整的半成品對(duì)象
- 三級(jí):lambada表達(dá)式
假設(shè)只設(shè)計(jì)二級(jí)緩存能否解決循環(huán)依賴?
- 只用二級(jí)緩存是可以解決緩存依賴的,(廢棄第三級(jí),保留第一第二)但是會(huì)有一個(gè)問題,在配置AOP切面的時(shí)候會(huì)出錯(cuò),因?yàn)闊o(wú)法生成代理對(duì)象。
- 所以三級(jí)緩存是為了處理AOP中的循環(huán)依賴。因?yàn)楫?dāng)配置了切面之后,在getEarlyBeanReference方法中,有可能會(huì)把之前的原始對(duì)象替換成代理對(duì)象,導(dǎo)致Bean的版本不是最終的版本,所以報(bào)錯(cuò)。
我是敖丙,你知道的越多,你不知道的越多,下期見。
新聞名稱:堂妹讓我聊:Spring循環(huán)依賴
文章位置:http://m.fisionsoft.com.cn/article/cdcphph.html


咨詢
建站咨詢
