1. 程式人生 > >緩存中獲取單例bean

緩存中獲取單例bean

trigge rac ets 頻率 性能 result cau str 一個

前言

上一篇文章FactoryBean的使用實際上是為了Bean的加載的詳細解析進行的介紹FactoryBean,從這篇文章開始,LZ會對Bean的加載過程進行詳細的講述,之前文章Bean的加載只是對Bean的加載過程進行了快速的大致上的過了一遍,詳細的解析過程開始。。。

緩存中獲取單例bean

前面的文章Bean的加載已經提到過,單例在Spring的同一個容器中只會被創建一次,後續再獲取bean直接從單例緩存中獲取,當然這裏也只是嘗試加載,首先嘗試從緩存中加載,然後再嘗試從singletonFactories中加載。因為在創建單例bean的時候會存在依賴註入的情況,而在創建依賴的時候為了避免循環依賴,Spring創建bean的原則是不等bean創建完成就會將創建bean的ObjectFactory提早曝光加入到緩存中,一旦下一個bean創建的時候需要依賴上個bean,則直接使用ObjectFactory。這句代碼在Bean加載的源碼中是(太多所以摘選):

Object sharedInstance = getSingleton(beanName);

跟蹤源代碼:

public Object getSingleton(String beanName) {
        //參數設置為true,表示允許早期依賴
        return getSingleton(beanName, true);
    }
 1 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 2         //檢查緩存中是否存在依賴
 3         Object singletonObject = this
.singletonObjects.get(beanName); 4 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 5 //如果為空,則鎖定全局變量並進行處理 6 synchronized (this.singletonObjects) { 7 //如果此bean正在加載則不處理 8 singletonObject = this.earlySingletonObjects.get(beanName);
9 if (singletonObject == null && allowEarlyReference) { 10 //當某些方法需要提前初始化的時候則會調用addSingletonFactory方法將對應的objectFactory初始化策略存儲在singletonFactories裏面 11 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 12 if (singletonFactory != null) { 13 //調用預先設定的getObject方法 14 singletonObject = singletonFactory.getObject(); 15 //記錄在緩存中,earlySingletonObjects和singletonFactories互斥 16 this.earlySingletonObjects.put(beanName, singletonObject); 17 //互斥,所以移除 18 this.singletonFactories.remove(beanName); 19 } 20 } 21 } 22 } 23 return singletonObject; 24 }

上述的代碼因為涉及循環依賴的檢測,以及涉及很多的變量的記錄存取,說明一下邏輯:

  (1)第3行:首先嘗試從singletonObjects裏面獲取實例;

  (2)第8行:如果從singletonObjects裏面獲取不到實例,則從earlySingletonObjects裏面獲取;

  (3)第11行:如果經過上述兩步還是沒有獲取到,則嘗試從singletonFactories裏面獲取beanName對應的ObjectFactory;

  (4)第14~18行:調用從singletonFactories裏面獲取到的ObjectFactory裏的getObject()方法來創建bean,並將其放到earlySingletonObjects裏面去,並且從singletonFactories裏面的這個對象remove掉(第3、4步都是為了循環依賴檢測時候使用,即allowEarlyReference為true)。

  (5)第23行:返回Object。

下面對上述出現的用於存儲bean的不同的map,進行一下簡單的解釋:

  ? singletonObjects:用於保存beanName和創建bean實例之間的關系(beanName --> bean instance);

  ? singletonFactories:用於保存beanName和創建bean的工廠之間的關系(beanName --> ObjectFactory);

  ? earlySingletonObjects:也是用於保存beanName和創建bean實例之間的關系,與singletonObjects的不同之處在於,當一個單例bean被放到earlySingletonObjects後,那麽當bean還在創建的過程中,就可以通過getBean方法獲取到了,其目的是用來檢測循環用,同時singletonObjects和earlySingletonObjects之間互斥,即單例bean只能在earlySingletonObjects和singletonObjects之中的一個,不能兩者都存在;

  ? registeredSingletons:用來保存當前所有已註冊的bean;

從bean的實例中獲取對象

經過上述從緩存中獲取單例bean之後,源代碼中bean的加載也就是getBean就會走到:

bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

在getBean()整個方法中,getObjectForBeanInstance()方法是個高頻率使用的方法,無論是從緩存中獲得bean還是根據不同的scope策略加載bean。總之,我們得到bean的實例後要做的第一步就是調用這個方法來檢測一下正確性,其實就是用於檢測當前bean是否是FactoryBean類型的bean,如果是,那麽需要調用該bean對應的FactoryBean實例中的getObject()方法作為返回值。

無論是從緩存中獲取到的bean還是通過不同的scope策略加載的bean都只是最原始的bean狀態,並不一定是我們最終想要的bean。舉個例子:假如我們需要對工廠bean進行處理,那麽這裏得到的其實是工廠bean的初始狀態,但是我們真正需要的是工廠bean中定義的factory-method方法中返回的bean,而getObjectForBeanInstance方法就是完成這個工作的。

跟蹤源代碼:

protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

        //如果指定的name是工廠相關(以&為前綴)且beanInstance又不是FactoryBean類型則驗證不通過
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            if (beanInstance instanceof NullBean) {
                return beanInstance;
            }
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
            }
        }

        // 現在我們有了個bean實例,這個實例可能會是正常的bean或者是FactoryBean,如果是FactoryBean我們使用它創建實例,
        //但是如果用戶想要直接獲取工廠實例而不是工廠的getObject方法對應的實例,那麽傳入的name應該加入前綴
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }
        //加載FactoryBean開始---------------
        Object object = null;
        if (mbd == null) {
            //嘗試從緩存中加載bean
            object = getCachedObjectForFactoryBean(beanName);
        }
        if (object == null) {
            // 到這裏就已經明確知道beanInstance一定是FactoryBean類型
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            // containsBeanDefinition檢查BeanDefinitionMap中也就是在所有已經加載的類中檢測是否定義beanName
            if (mbd == null && containsBeanDefinition(beanName)) {
                //將存儲XML配置文件的GenericBeanDefinition轉換為RootBeanDefinition,如果指定beanName是子bean的話同時合並父類的信息
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            //是否是用戶定義的而不是應用程序本身定義的
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }

從上面的代碼來看,其實這個方法並沒有什麽重要的信息,大多是些輔助代碼以及一些功能性的判斷。由於已經註釋了這麽多,所以這裏就不再贅述邏輯了。而真正的核心代碼卻委托給了getObjectFromFactoryBean方法,來看一下這個方法的源碼:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
        //如果是單例模式
        if (factory.isSingleton() && containsSingleton(beanName)) {
            synchronized (getSingletonMutex()) {
                Object object = this.factoryBeanObjectCache.get(beanName);
                if (object == null) {
                    object = doGetObjectFromFactoryBean(factory, beanName);
                    // Only post-process and store if not put there already during getObject() call above
                    // (e.g. because of circular reference processing triggered by custom getBean calls)
                    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                    if (alreadyThere != null) {
                        object = alreadyThere;
                    }
                    else {
                        if (shouldPostProcess) {
                            if (isSingletonCurrentlyInCreation(beanName)) {
                                // Temporarily return non-post-processed object, not storing it yet..
                                return object;
                            }
                            beforeSingletonCreation(beanName);
                            try {
                                object = postProcessObjectFromFactoryBean(object, beanName);
                            }
                            catch (Throwable ex) {
                                throw new BeanCreationException(beanName,
                                        "Post-processing of FactoryBean‘s singleton object failed", ex);
                            }
                            finally {
                                afterSingletonCreation(beanName);
                            }
                        }
                        if (containsSingleton(beanName)) {
                            this.factoryBeanObjectCache.put(beanName, object);
                        }
                    }
                }
                return object;
            }
        }
        else {
            Object object = doGetObjectFromFactoryBean(factory, beanName);
            if (shouldPostProcess) {
                try {
                    object = postProcessObjectFromFactoryBean(object, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(beanName, "Post-processing of FactoryBean‘s object failed", ex);
                }
            }
            return object;
        }
    }

發現這個方法還是沒有做什麽事情,只是做了一件事:返回的bean如果是單例的,那就必須保證全局唯一性,同時也是由於是單例的,所以沒有必要重復創建,可以使用緩存來提高性能。也就是說已經加載過的就要記錄下來以便於下次復用。還發現其重要的事情還是交給了doGetObjectFromFactoryBean方法:

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
            throws BeanCreationException {

        Object object;
        try {
            //權限驗證
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = getAccessControlContext();
                try {
                    object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                //直接調用getObject方法
                object = factory.getObject();
            }
        }
        catch (FactoryBeanNotInitializedException ex) {
            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }

        if (object == null) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(
                        beanName, "FactoryBean which is currently in creation returned null from getObject");
            }
            //返回空bean
            object = new NullBean();
        }
        return object;
    }

之前已經講述了FactoryBean的調用方法,如果bean聲明為FactoryBean類型,則當提取bean時提取的並不是FactoryBean,而是FactoryBean中對應的getObject方法返回的bean,而doGetObjectFromFactoryBean方法正是實現這個功能的。在通過getObject方法得到我們想要的結果後,並沒有立即返回而是經過了postProcessObjectFromFactoryBean方法:

AbstractAutowireCapableBeanFactory類裏的方法:

protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
        return applyBeanPostProcessorsAfterInitialization(object, beanName);
    }
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

這個方法是ObjectFactory父後處理器,以後會詳細講述,這裏只需要了解在Spring中獲取bean的規則中有這樣一條:盡可能保證所有bean在初始化後都會調用註冊的BeanPostProcessor的postProcessAfterInitialization方法進行處理。在實際的開發中,可以利用此特性設計自己的業務邏輯。

至此,從緩存中獲取單例的過程結束。

參考:《Spring源碼深度解析》 郝佳 編著:

緩存中獲取單例bean