說到spring,相信對於BeanFactory大家肯定都不陌生,百度上隨便一搜,99都會說什麼字如其意:bean工廠啊,容器,管理bean的。。。對於BeanFactory其實就是這樣子的,這裏我說的肯定沒有別人好,這邊就不一一贅述了。今天這邊主要來談談FactoryBean。
先來看一個顛覆你三觀的案例:
@Service
public class FactoryBeanTest implements FactoryBean<TestSingleton> {
@Override
public TestSingleton getObject() throws Exception {
return new TestSingleton();
}
@Override
public Class<?> getObjectType() {
return TestSingleton.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
我們交給spring管理了一個bean:FactoryBeanTest(beanid:factoryBeanTest ) ,但是我們根據annotationConfigApplicationContext去get這個bean的時候,拿到的卻是:TestSingleton
如果我們在beanid前加一個 & ,就可以拿到FactoryBeanTest這個對象了。
細心的哥哥們可能注意到了,我們的FactoryBeanTest 實現了:FactoryBean 接口,那麼我們就來分析下,這個FactoryBean 接口,以及spring容器在初始化時,是怎麼處理這個接口的。
這個接口有三個方法:
//指定創建的具體bean對象的類型
@Nullable
Class<?> getObjectType();
//設置創建的對象是否單例,可以不重寫默認是單例的,如果需要設置原型需要重寫此方法,返回false
default boolean isSingleton() {
return true;
}
//創建對象bean 具體創建具體對象是由此getObject()方法來創建並返回的
@Nullable
T getObject() throws Exception;
那麼問題來了:
1,spring在初始化的時候到底做了什麼,將原本是FactoryBeanTest的bean替換成了TestSingleton
2,爲什麼annotationConfigApplicationContext.getBean("&factoryBeanTest")可以拿到FactoryBeanTest
下面來看看spring容器初始化的時候到底做了什麼:
首先看下單例的FactoryBean
在spring容器初始化完成,創建bean實例的時候,方法調用入口如下:
org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
可以看到,我們這裏有13個beanname,5個是我們自己配置的,另外八個事spring內置的一些bean,我們不做關注,這邊直接看
factoryBeanTest。
String FACTORY_BEAN_PREFIX = "&";
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
上面這段是初始化源碼,很容易看到了,在根據beanname獲取到這個bean的BeanDefinition後,會判斷這個bean是不是一個factoryBean,如果是,則使用 &+beanname去做getBean操作。至於這個getBean細節不做詳細贅述,意思是如果根據beanname可以獲取到Object則直接返回,如果get不到,會基於當前beanname去做一個createBean操作,這樣就解釋了上述問題2:爲什麼annotationConfigApplicationContext.getBean("&factoryBeanTest")可以拿到FactoryBeanTest
那麼我們在回到第一個問題,在annotationConfigApplicationContext.getBean(“factoryBeanTest”)的時候,爲什麼拿到的是TestSingleton,繼續往下斷點,
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
在這裏我們單獨拿出這個getObjectFromFactoryBean(factory, beanName, !synthetic);方法,發現出來的就是我們的TestSingleton對象了,那麼繼續往下看:
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
關鍵的地方找到了,我來解釋下,就是再容器初始話的時候調用getbean的時候會去創建一次bean,再創建的時候發現如果這個bean是FactoryBean類型的,則會調用FactoryBean的getObject方法,得到這個方法返回的對象,並且如果這個bean是單例的,會緩存到一個名爲factoryBeanObjectCache的ConcurrentHashMap中,key(beanname)就是當前FactoryBean的beanname,下次拿的時候如果是原型的,則每次都會去調用FactoryBean的getObject方法拿到原型對象,如果是單例的,則直接在緩存factoryBeanObjectCache中根據beanname去拿到單例對象並返回。
據此,我們問題一也通過源碼很好的解釋了。(補充一句,在我們的spring-mybatis的核心源碼中就使用了FactoryBean,有時間再來和大家分享一下!)
好了今天弄清楚了FactoryBean的相關問題,以及從源碼的角度去分析了FactoryBean的作用以及spring內部是如何運行的,希望對大家有所幫助,同時有不足之處還望大家多多指正!