Spring源碼分析系列-循環依賴和三級緩存 目錄 循環依賴 多級緩存 一級緩存 二級緩存 當循環依賴遇上AOP 三級緩存 Spring三級緩存源碼實現 總結

目錄

循環依賴

BeanFactory作爲bean工廠管理我們的單例bean,那麼肯定需要有個緩存來存儲這些單例bean,在Spring中就是一個Map結構的緩存,key爲beanName,value爲bean。在獲取一個bean的時候,先從緩存中獲取,如果沒有獲取到,那麼觸發對象的實例化和初始化操作,完成之後再將對象放入緩存中,這樣就能實現一個簡單的bean工廠。

由於Spring提供了依賴注入的功能,支持將一個對象自動注入到另一個對象的屬性中,這就可能出現循環依賴(區別於dependsOn)的問題:A對象在創建的時候依賴於B對象,而B對象在創建的時候依賴於A對象。

可以看到,由於A依賴B,所以創建A的時候觸發了創建B,而B又依賴A,又會觸發獲取A,但是此時A正在創建中,還不在緩存中,就引發了問題。

多級緩存

一級緩存

前面引出了循環依賴的問題,那麼該如何解決呢?其實很簡單,單單依賴一級緩存就能解決。對於一級緩存,我們不再等對象初始化完成之後再存入緩存,而是等對象實例化完成後就存入一級緩存,由於此時緩存中的bean還沒有進行初始化操作,可以稱之爲早期對象,也就是將A對象提前暴露了。這樣B在創建過程中獲取A的時候就能從緩存中獲取到A對象,最後在A對象初始化工作完成後再更新一級緩存即可,這樣就解決了循環依賴的問題。

但是這樣又引出了另一個問題:早期對象和完整對象都存在於一級緩存中,如果此時來了其它線程併發獲取bean,就可能從一級緩存中獲取到不完整的bean,這明顯不行,那麼我們不得已只能在從一級緩存獲取對象處加一個互斥鎖,以避免這個問題。

而加互斥鎖也帶來了另一個問題,容器刷新完成後的普通獲取bean的請求都需要競爭鎖,如果這樣處理,在高併發場景下使用spring的性能一定會極低。

二級緩存

既然只依賴一級緩存解決循環依賴需要靠加鎖來保證對象的安全發展,而加鎖又會帶來性能問題,那該如何優化呢?答案就是引入另一個緩存。這個緩存也是一個Map結構,key爲beanName,value爲bean,而這個緩存可以稱爲二級緩存。我們將早期對象存到二級緩存中,一級緩存還是用於存儲完整對象(對象初始化工作完成後),這樣在接下來B創建的過程中獲取A的時候,先從一級緩存獲取,如果一級緩存沒有獲取到,則從二級緩存獲取,雖然從二級緩存獲取的對象是早期對象,但是站在對象內存關係的角度來看,二級緩存中的對象和後面一級緩存中的對象(指針)都指向同一對象,區別是對象處於不同的階段,所以不會有什麼問題。

既然獲取bean的邏輯是先從一級緩存獲取,沒有的話再從二級緩存獲取,那麼也可能出現其它線程獲取到不完整對象的問題,所以還是需要加互斥鎖。不過這裏的加鎖邏輯可以下沉到二級緩存,因爲早期對象存儲在二級緩存中,從一級緩存獲取對象不用加鎖,這樣的話當容器初始化完成之後,普通的getBean請求可以直接從一級緩存獲取對象,而不用去競爭鎖。

當循環依賴遇上AOP

似乎二級緩存已經解決了循環依賴的問題,看起來也非常簡單,但是不要忘記Spring提供的另一種特性:AOP。Spring支持以CGLIB和JDK動態代理的方式爲對象創建代理類以提供AOP支持,在前面總結bean生命週期的文章中已經提到過,代理對象的創建(通常)是在bean初始化完成之後進行(通過BeanPostProcessor後置處理器)的,而且按照正常思維來看,一個代理對象的創建也應該在原對象完整的基礎上進行,但是當循環依賴遇上了AOP就不那麼簡單了。

還是在前面A和B相互依賴的場景中,試想一下如果A需要被代理呢?由於二級緩存中的早期對象是原對象,而代理對象是在A初始化完成之後再創建的,這就導致了B對象中引用的A對象不是代理對象,於是出現了問題。

要解決這問題也很簡單,把代理對象提前創建不就行了?也就是如果沒有循環依賴,那麼代理對象還是在初始化完成後創建,如果有循環依賴,那麼就提前創建代理對象。那麼怎麼判斷髮生了循環依賴呢?在B創建的過程中獲取A的時候,發現二級緩存中有A,就說明發生了循環依賴,此時就爲A創建代理對象,將其覆蓋到二級緩存中,並且將代理對象複製給B的對應屬性,解決了問題。當然,最終A初始化完成之後,在一級緩存中存放的肯定是代理對象。

如果在A和B相互依賴的基礎上,還有一個對象C也依賴了A:A依賴B,B依賴A,A依賴C,C依賴A。那麼在爲A創建代理對象的時候,就要注意不能重複創建。

可以在對象實例化完成之後,將其beanName存到一個Set結構中,標識對應的bean正在創建中,而當其他對象創建的過程依賴某個對象的時候,判斷其是否在這個set中,如果在就說明發生了循環依賴。

三級緩存

雖然僅僅依靠二級緩存能夠解決循環依賴和AOP的問題,但是從解決方案上看來,維護代理對象的邏輯和getBean的邏輯過於耦合,Spring沒有采取這種方案,而是引入了另一個三級緩存。三級緩存的key還是爲beanName,但是value是一個函數(ObjectFactory#getBean方法),在該函數中執行獲取早期對象的邏輯:getEarlyBeanReference方法。 在getEarlyBeanReference方法中,Spring會調用所有SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法,通過該方法可以修改早期對象的屬性或者替換早期對象。這個是Spring留給開發者的另一個擴展點,雖然我們很少使用,不過在循環依賴遇到AOP的時候,代理對象就是通過這個後置處理器創建。

Spring三級緩存源碼實現

那麼三級緩存在spring中是如何體現的呢?我們來到DefaultSingletonBeanRegistry中:

//一級緩存,也就是單例池,存儲最終對象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//二級緩存,存儲早期對象
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
//三級緩存,存儲的是一個函數接口,
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

其實所謂三級緩存,在源碼中就是3個Map,一級緩存用於存儲最終單例對象,二級緩存用於存儲早期對象,三級緩存用於存儲函數接口。

在容器刷新時調用的十幾個方法中,finishBeanFactoryInitialization方法主要用於實例化單例bean,在其邏輯中會凍結BeanDefinition的元數據,接着調用beanFactory的preInstantiateSingletons方法,循環所有被管理的beanName,依次創建Bean,我們這裏主要關注創建bean的邏輯,也就是AbstractBeanFactory的doGetBean方法(該方法很長,這裏只貼了部分代碼):

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        //解析FactoryBean的name(&)和別名
        final String beanName = transformedBeanName(name);
        Object bean;
        //嘗試從緩存中獲取對象
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            //包含了處理FactoryBean的邏輯,可以通過&+beanName獲取原對象,通過beanName獲取真實對象
            //FactoryBean的真實bean有單獨的緩存factoryBeanObjectCache(Map結構)存放
            //如果是普通的bean,那麼直接返回對應的對象
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        else {
            //只能解決單例對象的循環依賴
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
            //如果存在父工廠,並且當前工廠中不存在對應的BeanDefinition,那麼嘗試到父工廠中尋找
            //比如spring mvc整合spring的場景
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //bean的原始名稱
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }
            try {

                //處理dependsOn的依賴(不是循環依賴)
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            //循環depends-on 拋出異常
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }
                //創建單例bean
                if (mbd.isSingleton()) {
                    //把beanName 和一個ObjectFactory類型的singletonFactory傳入getSingleton方法
                    //ObjectFactory是一個函數,最終創建bean的邏輯就是通過回調這個ObjectFactory的getObject方法完成的
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            //真正創建bean的邏輯
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }
        return (T) bean;
    }

對於單例對象,調用doGetBean方法的時候會調用getSingleton方法,該方法的入參是beanName和一個ObjectFactory的函數,在getSingleton方法中通過回調ObjectFactory的getBean方法完成bean的創建,那我們來看看getSingleton方法(部分代碼):

    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) {
                //在創建之前將要創建的beanName存入singletonsCurrentlyInCreation中,這個是一個Map結構,用於標識單例正在創建中
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    //回調singletonFactory的getObject方法,該函數在外層doGetBean方法中傳入
                    //實際調用了createBean方法
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    //將beanName從正在創建單例bean的集合singletonsCurrentlyInCreation中移除
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    //將創建好的單例bean放入一級緩存,並且從二級緩存、三級緩存中移除
                    //添加到registeredSingletons中
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

getSingleton方法的主線邏輯很簡單,就是通過傳入的函數接口創建單例bean,然後存入一級緩存中,同時清理bean在二級緩存、三級緩存中對應的數據。前面已經分析過了,傳入的函數接口調用的是createBean方法,那麼我們又來到createBean方法,createBean方法主要調用doCreateBean方法,在doCreateBean調用之前會先調用nstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation攔截bean的實例化,如果這裏的後置處理器返回了bean,則不會到後面的doCreateBean方法中,不過我們這裏不用關心這個邏輯,直接跳到doCreateBean方法(部分代碼):

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //創建bean,也是就是實例化,會把實例化後的bean包裝在BeanWrapper中
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        //eanWrapper中獲取到對象
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    //MergedBeanDefinitionPostProcessor後置處理器的處理
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }
        //判斷該對象是否支持提前暴露,核心條件就是要求單例對象
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            //把我們的早期對象包裝成一個singletonFactory對象 該對象提供了一個getObject方法,該方法內部調用getEarlyBeanReference方法
            //將早期對象存入三級緩存,三級緩存存的是一個接口實現(ObjectFactory接口),其getBean方法會調用getEarlyBeanReference方法
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            //屬性賦值操作,這裏就可能出現循環依賴問題
            populateBean(beanName, mbd, instanceWrapper);
            //實例化操作:調用三個Aware、後置處理器beforeInitialization(包含@PostConstruct的實現)、afterPropertiesSet、init-method、後置處理器afterInitialization等
            //個方法裏的後置處理器可能會改變exposeObject
            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);
            }
        }
        if (earlySingletonExposure) {
            //這裏的入參爲false,表示只從一級緩存和二級緩存中獲取
            //非循環依賴的情況下,不會調用到三級緩存,三級緩存中的接口會在單例對象入一級緩存的時候移除
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                    //如果獲取到了早期暴露對象earlySingletonReference不爲空
                    if (exposedObject == bean) {
                        //如果exposeObject在經過initializeBean方法後沒有改變,那麼說明
                        //沒有被後置處理器修改,那麼用earlySingletonReference替換exposeObject
                        exposedObject = earlySingletonReference;
                }
            }
        }
        return exposedObject;
    }

這個方法的邏輯就是先實例化對象,如果對象支持暴露早期對象,那麼會將早期對象作爲入參傳入getEarlyBeanReference方法,然後包裝成一個ObjectFactory存入三級緩存,接着再調用populateBean方法填充對象的屬性。而在填充對象屬性的過程中,就可能發現循環依賴,比如當前正在創建A,然後在populateBean方法中發現了依賴B,那麼就會調用getBean(B),B也會走上面A走的這個流程,當B也走到populateBean方法填充屬性的時候,又發現依賴了A,那麼又會調用getBean(A)。那麼在populateBean創建A的時候,會從三級緩存中獲取bean,如果A需要被代理,那麼會創建代理對象,這樣B中的A屬性就是代理對象了,接着把三級緩存獲取的對象存入二級緩存中。

在B處理完成之後就回到了A的邏輯,假設populateBean(A)的邏輯完成了,那麼接着進入initializeBean方法進行A的初始化操作,注意這裏執行初始化操作的對象是A原對象,代理對象存儲在二級緩存中。由於initializeBean方法會調用後置處理器的before-afterInitialization方法,這個後置處理器可能會改變對象,所以在後面的邏輯中,如果從二級緩存中獲取到了A的代理對象,會判斷原對象經過後置處理器後有沒有變化,如果還是原對象,那麼用二級緩存中的代理對象覆蓋原對象,所以doCreateBean方法返回的就是代理對象,最終存入一級緩存中。流程就是:創建A實例對象->設置A的B屬性->創建B實例對象->設置B的A屬性->創建A的代理對象存入二級緩存->初始化A對象->從二級緩存取出A的代理對象覆蓋A對象->A的代理對象存入一級緩存

我們需要關注的就是在B的populateBean邏輯裏調用getBean(A)是什麼樣的邏輯。當然也是getBean->doGetBean->getSingleton這個邏輯,我們着重關注一下getSingleton方法的實現:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //首先從一級緩存中獲取
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //如果從一級緩存沒有獲取到,並且獲取的對象正在創建中,這裏已經能夠確定發生了循環依賴
            synchronized (this.singletonObjects) {
                //爲了防止其它線程獲取到不完整工單對象,這裏使用同步鎖控制
                //從二級緩存中獲取早期對象
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    //如果二級緩存中也沒有獲取到,且allowEarlyReference爲true(這裏傳入的是true)
                    //那麼嘗試從三級緩存中獲取
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //從三級緩存中獲取到了對象,注意三級緩存存儲的是ObjectFactory
                        //調用getObject方法,前面提到了早期暴露的對象存入三級緩存的ObjectFactory的getBean方法調用
                        //的是getEarlyBeanReference方法,所以這裏會調用getEarlyBeanReference方法
                        singletonObject = singletonFactory.getObject();
                        //把早期對象存入二級緩存
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        //三級緩存中的接口沒用了,直接移除
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

在getSingleton方法的邏輯中,先從一級緩存獲取,如果一級緩存沒有找到,那麼如果獲取的bean正在創建中,則從二級緩存獲取,如果二級緩存沒有找到,那麼從三級緩存獲取,三級緩存中存的是ObjectFactory實現,最終會調用其getBean方法獲取bean,然後存入二級緩存中,同時清除三級緩存。同時提供了一個allowEarlyReference參數控制是否能從三級緩存中獲取。

對於循環依賴的情況,getBean(A)->存入正在創建緩存->存入三級緩存->populateBean(A)->getBean(B)->populateBean(B)->getBean(A)->getSingleton(A),當在populateBean(B)的過程中調用getSingleton(A)的時候,明顯一級緩存和二級緩存都爲空,但是三級緩存不爲空,所以會通過三級緩存獲取bean,三級緩存的創建邏輯如下:

        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

所以我們直接來到getEarlyBeanReference方法獲取早期對象:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        //如果容器中有InstantiationAwareBeanPostProcessors後置處理器
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    //找到SmartInstantiationAwareBeanPostProcessor後置處理器
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    //調用SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

這個getEarlyBeanReference方法的邏輯很簡單,但是非常重要,該方法主要就是對SmartInstantiationAwareBeanPostProcessor後置處理器的調用,而循環依賴時的AOP就是通過這個SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法實現的,相關的具體類是AnnotationAwareAspectJAutoProxyCreator,這個留待AOP原理分析的文章中詳細說明。

總結

多級緩存並不只是爲了解決循環依賴和AOP的問題,還考慮到了邏輯的分離、結構的擴展性和保證數據安全前提下的效率問題等。由於存在二級緩存提前暴露不完整對象的情況,所以爲了防止getSingleton方法返回不完整的bean,在該方法中使用了synchronized加鎖,而爲了不影響容器刷新完成後正常獲取bean,從一級緩存獲取對象沒有加鎖,事實上也不用加鎖,因爲一級緩存中要麼沒有對象,要麼就是完整的對象。

通常情況下,容器刷新會觸發單例對象的實例化和初始化,大致流程如下:

循環依賴發生在對象都屬性賦值,也就是populateBean階段,在對象實例化完成後將其包裝成一個函數接口ObjectFactory存入三級緩存,通過getEarlyBeanReference方法觸發SmartInstantiationAwareBeanPostProcessor後置處理器的執行,如果循環依賴遇上了AOP,那麼就在這裏進行處理。

這裏每個單例對象實例化之後存入三級緩存,即使沒有發生循環依賴,這個是爲之後可能發生的循環依賴做準備,如果最終沒有發生循環依賴,那麼對象實例化之後,正常初始化,然後存入一級緩存即可,而在存入一級緩存的時候會清空二級和三級緩存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章