從源碼角度聊聊spring中的FactoryBean

說到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內部是如何運行的,希望對大家有所幫助,同時有不足之處還望大家多多指正!

發佈了47 篇原創文章 · 獲贊 97 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章