有關Spring加載bean系列,今天這是最后一篇了,主要接上篇對于從Spring容器中獲取Bean的一些細節實現的補充。
《Spring讀書筆記——bean加載》——Spring如何加載消化一個xml配置文件
《Spring讀書筆記——bean解析》——Spring如何將xml文件的各種標簽轉換為BeanDefinition并注冊到Spring容器下
《Spring讀書筆記——bean創建(上)》——概述Spring如何從容器中取出需要的那個Bean
從緩存中加載單例
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) {
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
在看這段代碼之前,我們先了解下Spring對于單例bean出現循環依賴的解決方法。如果出現上面我們介紹的A->B->C->A的情況,那是不是說Spring就無能為力了,顯然Spring沒有那么弱。那么Spring是怎么做的?
鑒于單例bean的循環依賴問題,Spring創建bean的原則是不等bean創建完成就會將bean的ObjectFactory提前曝光加入到緩存中,一旦有某個bean創建時需要依賴這個bean了,那么就可以直接使用ObjectFactory。
簡單說,創建bean的時候,就是打包快遞發貨,主管為了知道你今天要派發多少個包裹,為了節省大家時間以及以免統計漏掉的情況。你可以先拿出一個包裹箱子,上面寫上要寄收件人、收貨地址、聯系方式等等,但是這時候還沒有往里面打包真正的快遞。
這里曝光的bean就相當于這個快遞箱子。
好了,知道了這個原則之后,我們就好理解代碼了。
首先從singletonObjects中獲取實例,取不到則從earlySingletonObjects中獲取,仍然取不到,我們還可以到singletonFactories中獲取相應的ObjectFactory,在調用這個ObjectFactory的getObject方法來創建bean。
然后將其加入到earlySingletonObjects中,在將其從singletonFactories中刪除。
想必,你已經被這些用來存儲和刪除的集合搞瘋了,沒關系,我們來理一下:
singletonObjects
/** Cache of singleton objects: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
用于保存BeanName和創建bean實例之間的關系。
singletonFactories:
/** Cache of singleton factories: bean name --> ObjectFactory */ private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);
用于保存BeanName和創建bean的工廠之間的關系
earlySingletonObjects:
/** Cache of early singleton objects: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
用于保存BeanName和創建bean的工廠之間的關系,與singletonObjects的區別是當一個bean被放入這個集合后,可以用于其他bean做循環依賴檢查
bean實例化
我們從緩存中拿到bean之后,就需要根據bean的不同類型做不同的處理,返回相應的bean,實現這個功能的就是getObjectForBeanInstance方法
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
- 首先檢測指定的beanName是否是工廠bean相關,如果既不是工廠bean又是以"&"開頭,則校驗失敗,拋出異常
- 如果這個bean不是工廠Bean(FactoryBean),那么就直接返回bean實例
剩下代碼就是處理FactoryBean,我們順著這樣的順序依次來到getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean
private Object doGetObjectFromFactoryBean( final FactoryBean factory, final String beanName, final boolean shouldPostProcess) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { return factory.getObject(); } }, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { 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); } // Do not accept a null value for a FactoryBean that's not fully // initialized yet: Many FactoryBeans just return null then. if (object == null && isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } if (object != null && shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex); } } return object; }
這么長的代碼,如果嫌累,就只看factory.getObject()這行就好,這詮釋了FactoryBean的加載時通過factory.getObject的方式獲取到對應的bean實例的。
如何創建單例bean
在上篇的doGetBean方法中,如果從緩存中加載不到,那么我們就需要老老實實的從頭開始加載bean了,對于單例bean的加載就都在這里實現了
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
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);
}
創建bean
我們從AbstractBeanFactory的createBean方法來到了AbstractAutowiredCapableBeanFactory的createbean方法,而真正的創建bean其實在doCreateBean方法中
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
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) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
- 如果是單例Bean,那么首先是從factoryBeanInstanceCache中清除該beanName對應的記錄
- 實例化bean,將BeanDefinition轉換為BeanWrapper對象
- bean合并后的處理
- 解決循環依賴問題
- 屬性填充,將所有屬性填充到bean的實例中
這個方法,本身不算長,但是層層深入就會發現其下面包羅了創建bean的諸多繁雜的細節(這塊自己看的也是云里霧里,就不往下延伸擴展了)。
雖然對于Spring加載bean,我只寫了四篇,但是其內部實現遠比我表述的要復雜的多。
看源碼確實很煎熬,對于目前看不懂的地方要么多看幾遍,要么先跳過。閱讀代碼的過程中要懂得取舍,對于非重點部分比如日志或者異常處理可以先忽略,沿著一條主線往下看,最主要是先弄懂代碼的只要意圖。
Spring的bean加載代碼量雖然巨大,但是思路還是比較清晰的,我們知道Spring如何加載xml然后解析xml,再到如何把xml的元素轉為自己的BeanDefinition,最后又是如何取出對應的beanName然后返回一個bean實例供容器使用的。
網上有一位大神用一張圖就把整個過程畫出來了
注:圖片來源http://blog.csdn.net/zghwaicsdn/article/details/50910384
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,并和您一起分享我日常閱讀過的優質文章。
文章列表
留言列表