通過《Spring讀書筆記——bean加載》和《Spring讀書筆記——bean解析》,我們明白了兩件事。
- Spring如何加載消化一個xml配置文件
- Spring如何將xml文件的各種標簽轉換為BeanDefinition并注冊到Spring容器下
現在,我們理所當然的還差bean是如何被創建出來這一環節了。
從getBean說起
我們經常使用下面的方式實現先加載xml文件,然后獲取相應的bean實例
BeanFactory beanFactory = new ClassPathXmlApplicationContext("application-context.xml");
TestBean testBean = beanFactory.getBean("testBean");
顯然,我們是通過getBean方法獲取到的Bean實例,該方法是接口BeanFactory中定義的一個方法。具體實現在另外一個抽象類中,就是我們一會要說到的AbstractBeanFactory。
AbstractBeanFactory
該抽象類集成了FactoryBeanRegistrySupport并實現了ConfigurableBeanFactory接口(該接口間接實現了接口BeanFactory)
通過上面的區塊注釋以及提供的方法getBean,我們一眼就看出其余BeanFactory的密切關系。
getBean
該方法非常簡單,只是調用了一個函數,真正的實現都在doGetBean方法中了。代碼實現有點長,但是我們還是得靜下心來看看他到底做了哪些工作。
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
// 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);
}
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
歸一化beanName
如果看了前面兩篇文章,這里你就知道beanName就是注冊到Spring容器中的bean的名稱,具體來說就是放入BeanDefinitionMap中的一個鍵值對的key。
那么這里有什么好轉換處理的呢。
我們看下transformedBeanName的具體實現
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}
沒錯,做了一個斷言處理,通過對于一種FactoryBean形式的bean做了處理。從FactoryBean這個命名就知道他是一種bean(不是病),*有關FactoryBean的介紹以及使用可以參看博文:http://blog.csdn.net/is_zhoufeng/article/details/38422549*,區別于BeanFactory(他是一個bean工廠,用于生產bean)。FactoryBean這種在注入bean的時候會在beanName前添加一個"&"修飾符,所以這里需要做歸一化處理。
嘗試從緩存中加載單例bean
Object sharedInstance = getSingleton(beanName);
該方法就是實現從緩存中獲取單例bean。
對于單例,應該都不陌生,單例bean,單例模式等等說的都是一個意思——一個東西只有一份。
- Spring默認創建的bean就是單例bean,也就是在容器中只會存在一份這樣的bean
- 這只是一次嘗試加載,如果加載不到,通過后面的代碼,我們可以發現其會從singletonFactories中加載
- 加載單例bean可能會遇到一個頭疼的問題——循環依賴,就是加載A時,A依賴了B,那么需要加載B,發現B依賴了C,這時候去加載C,發現C依賴了A,這樣就形成了一個閉環,也就是循環依賴A->B->C-A。后面會說Spring是如何解決這個問題的
- 有關Spring中不同類型的bean的循環依賴問題和解決方法可以參看*https://my.oschina.net/yibuliushen/blog/737640*
bean實例化
假設我們從緩存中得到了bean,但是這還不是bean的最終狀態,可以認為這只是一個引用,要獲得真正的bean實例,我們還需要看下getObjectForBeanInstance方法。
原型模式的循環依賴檢查
這里引用下上面提供有關循環依賴的鏈接中比較重要的內容
spring循環依賴的情況
1.構造器注入屬性依賴(A B兩個對象都使用構造方法,注入依賴的屬性)
無論是單例,還是原型對象,只要是通過構造器注入的屬性依賴,都會報錯,循環依賴錯誤 org.springframework.beans.factory.BeanCurrentlyInCreationException:
原因:試想,構造器是創建對象的入口方法,構造的時候都循環依賴了,我這個對象壓根就創建不了啊。那肯定是無法解決的,大羅神仙也無能為力。
2.setter方法注入屬性依賴
這個spring完美解決了,支持這種循環依賴
原理:創建對象A的時候,先通過無參構造方法創建一個實例,此時屬性都是空的,但是對象引用已經創建出來,然后把A的引用提前暴露出來。然后setter B屬性的時候,創建B對象,此時同樣通過無參構造方法構造然后將對象引用暴露出來。接著B執行setter方法,去池中找A,能找到A(因為此時A已經暴露出來,有指向改對象的引用了),這么依賴B就構造完成,也初始化完成,然后A接著初始化完成。---循環依賴就這么解決了
3.原型對象的屬性依賴(當然指的是通過setter方法注入依賴)
這個spring也無能為力,因為是原型對象,A創建的時候不會提前暴露出來,所以,每次都是要創建,創建的時候,發現有相同的對象正在創建,同樣報錯,循環依賴錯誤,同第一種情況類似。
我們知道對于單例默認的循環依賴,我們是可以解決的,但是對于原型類型的循環依賴,我們沒有辦法解決,所以這里通過對于原型bean的檢查適時拋出異常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
檢測parentBeanFactory
順著代碼的邏輯,很自然的來到了從parentBeanFactory中加載Bean的模塊。當在緩存中沒有加載到Bean的時候,我們就會從parentBeanFactory中試試。
轉換為RootBeanDefinition
前兩篇,我們介紹了從xml標簽到BeanDefinition的轉變,這里我們需要將GenericBeanDefinition轉為RootBeanDefinition,這是處于后面的處理是一致對于RootBeanDefinition處理考量的。
解決bean依賴的問題
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
從注釋就可以知道,這塊主要是解決在初始化一個bean的時候,這個bean依賴了其他的bean,所以需要在創建之前先初始化依賴的bean。
對于scope的處理以及對于類型轉換的處理
后面剩下的代碼主要是對于Spring中不同scope的處理,比如singleton、prototype、request、session等等。
上面就是getBean這一抽象層次上關于如何創建bean的詳細過程,下面對于其中一些部分做詳細解釋。
等等,寫著寫著發現后面的點還是有點多,一篇恐怕撐不住了,還是分上下集吧~~~
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,并和您一起分享我日常閱讀過的優質文章。
文章列表