緩存中獲取單例bean
前言
上一篇文章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