在上篇文章中咱們已經對容器的第一個擴展點(BeanFactoryPostProcessor)作了一系列的介紹。其中主要介紹了Spring容器中BeanFactoryPostProcessor的執行流程。已經Spring自身利用了BeanFactoryPostProcessor完成了什麼功能,對於一些細節問題可能說的不夠仔細,可是在當前階段我想要作的主要是爲咱們之後學習源碼打下基礎。因此對於這些問題咱們暫且不去過多糾結,待到源碼學習階段咱們會進行更加細緻的分析。在本篇文章中,咱們將要學習的是容器的另外一個擴展點(FactoryBean),對於FactoryBean官網上的介紹甚短,可是若是咱們對Spring的源碼有必定了解,能夠發現Spring在不少地方都對這種特殊的Bean作了處理。話很少說,咱們開始進入正文。面試
咱們仍是先看看官網上是怎麼說的:緩存
從上面這段文字咱們能夠得出如下幾個信息:mybatis
上面這些概念可能剛剛說的時候你們不是很明白,下面咱們經過FactoryBean的一些應用來進一步體會這個接口的做用。ide
咱們來看下面這個Demo:源碼分析
public class MyFactoryBean implements FactoryBean { @Override public Object getObject() throws Exception { System.out.println("執行了一段複雜的建立Bean的邏輯"); return new TestBean(); } @Override public Class<?> getObjectType() { return TestBean.class; } @Override public boolean isSingleton() { return true; }}public class TestBean { public TestBean(){ System.out.println("TestBean被建立出來了"); }}// 測試類public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ac= new AnnotationConfigApplicationContext(Config.class); System.out.println("直接調用getBean(\"myFactoryBean\")返回:"+ac.getBean("myFactoryBean")); System.out.println("調用getBean(\"&myFactoryBean\")返回:"+ac.getBean("&myFactoryBean")); }}
運行後結果以下:學習
執行了一段複雜的建立Bean的邏輯 TestBean被建立出來了 直接調用getBean("myFactoryBean")返回:com.dmz.official.extension.factorybean.TestBean@28f67ac7 調用getBean("&myFactoryBean")返回:com.dmz.official.extension.factorybean.MyFactoryBean@256216b3
咱們雖然沒有直接將TestBean放入Spring容器中,可是經過FactoryBean也完成了這一操做。同時當咱們直接調用getBean("FactoryBean的名稱")獲取到的是FactoryBean建立的Bean,可是添加了「&」後能夠獲取到FactoryBean自己。測試
咱們先看下下面這張圖:ui
涉及到FactoryBean主要在3-11-6這一步中,咱們主要關注下面這段代碼:this
// .....省略無關代碼.......// 1.判斷是否是一個FactoryBeanif (isFactoryBean(beanName)) { // 2.若是是一個FactoryBean那麼在getBean時,添加前綴「&」,獲取這個FactoryBean Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; // 3.作權限校驗,判斷是不是一個SmartFactoryBean,而且不是懶加載的 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { // 3.判斷是不是一個SmartFactoryBean,而且不是懶加載的 isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { // 4.若是是一個SmartFactoryBean而且不是懶加載的,那麼建立這個FactoryBean建立的Bean getBean(beanName); } }}else { // 不是一個FactoryBean,直接建立這個Bean getBean(beanName);}// ...省略無關代碼.....
咱們按照順序一步步分析,首先看第一步:spa
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException { String beanName = transformedBeanName(name); // 直接從單例池中獲取這個Bean,而後進行判斷,看是不是一個FactoryBean Object beanInstance = getSingleton(beanName, false); if (beanInstance != null) { return (beanInstance instanceof FactoryBean); } // 查找不到這個BeanDefinition,那麼從父容器中再次確認是不是一個FactoryBean if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) { // No bean definition found in this factory -> delegate to parent. return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name); } // 從當前容器中,根據BeanDefinition判斷是不是一個FactoryBean return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));}
這裏涉及到一個概念,就是SmartFactoryBean,實際上這個接口繼承了FactoryBean接口,而且SmartFactoryBean是FactoryBean的惟一子接口,它擴展了FactoryBean多提供了兩個方法以下:
// 是否爲原型,默認不是原型default boolean isPrototype() { return false;}// 是否爲懶加載,默認爲懶加載default boolean isEagerInit() { return false;}
從上面的代碼中能夠看出,咱們噹噹實現一個FactoryBean接口,Spring並不會在啓動時就將這個FactoryBean所建立的Bean建立出來,爲了不這種狀況,咱們有兩種辦法:
實際上咱們能夠發現,當咱們僅僅實現FactoryBean時,其getObject()方法所產生的Bean,咱們能夠當前是懶加載的。
在上面的代碼分析完後,在3-6-11-2中也有兩行FactoryBean相關的代碼,以下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 1.獲取bean名稱 final String beanName = transformedBeanName(name); Object bean; //...省略無關代碼...,這裏主要根據beanName建立對應的Bean // 2.調用getObject對象建立Bean bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); }
protected String transformedBeanName(String name) { // 這個方法主要用來解析別名,若是是別名的話,獲取真實的BeanName return canonicalName(BeanFactoryUtils.transformedBeanName(name));} // 處理FactoryBeanpublic static String transformedBeanName(String name) { Assert.notNull(name, "'name' must not be null"); // 沒有帶「&」,直接返回 if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { return name; } // 去除全部的「&」,防止這種寫法getBean("&&&&beanName") return transformedBeanNameCache.computeIfAbsent(name, beanName -> { do { beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); return beanName; });}
咱們能夠看到,在調用getObjectForBeanInstance(sharedInstance, name, beanName, null);傳入了一個參數---name,也就是尚未通過transformedBeanName方法處理的bean的名稱,可能會帶有「&」符號,Spring經過這個參數判斷這個Bean是否是一個FactoryBean,若是是的話,會調用其getObject()建立Bean。被建立的Bean不會存放於單例池中,而是放在一個名爲factoryBeanObjectCache的緩存中。具體的代碼由於比較複雜,在這裏咱們就暫且不分析了,你們能夠先留個印象,源碼階段我會作詳細的分析。
除了咱們在上文中說到的實現了FactoryBean或者SmartFactoryBean接口的Bean能夠稱之爲一個」FactoryBean「,不知道你們對BeanDefinition中的一個屬性是否還有印象。BeanDefinition有屬性以下(實際上這個屬性存在於AbstractBeanDefinition中):
@Nullableprivate String factoryBeanName;@Nullableprivate String factoryMethodName;
對於這個屬性跟咱們這篇文章中介紹的FactoryBean有什麼關係呢?
首先,咱們看看什麼狀況下bd中會存在這個屬性,主要分爲如下兩種狀況:
第一種狀況:
@Configurationpublic class Config { @Bean public B b(){ return new B(); }}
咱們經過@Bean的方式來建立一個Bean,那麼在B的BeanDefinition會記錄factoryBeanName這個屬性,同時還會記錄是這個Bean中的哪一個方法來建立B的。在上面的例子中,factoryBeanName=config,factoryMethodName=b。
第二種狀況:
<bean id="factoryBean" class="com.dmz.official.extension.factorybean.C"/><bean id="b" class="com.dmz.official.extension.factorybean.B" factory-bean="factoryBean" factory-method="b"/>
經過XML的方式進行配置,此時B的BeanDefinition中factoryBeanName=factoryBean,factoryMethodName=b。
上面兩種狀況,BeanDefinition中的factoryBeanName這個屬性均不會爲空,可是請注意此時記錄的這個名字因此對於的Bean並非一個實現了FactoryBean接口的Bean。
綜上,咱們能夠將Spring中的FactoryBean的概念泛化,也就是說全部生產對象的Bean咱們都將其稱爲FactoryBean,那麼能夠總結畫圖以下:
這是我的觀點哈,沒有在官網找到什麼文檔,只是這種比較學習更加能加深印象,因此我把他們作了一個總結,你們面試的時候不用這麼說
FactoryBean就如咱們標題所說,是Spring提供的一個擴展點,適用於複雜的Bean的建立。mybatis在跟Spring作整合時就用到了這個擴展點。而且FactoryBean所建立的Bean跟普通的Bean不同。咱們能夠說FactoryBean是Spring建立Bean的另一種手段。
而BeanFactory是什麼呢?BeanFactory是Spring IOC容器的頂級接口,其實現類有XMLBeanFactory,DefaultListableBeanFactory以及AnnotationConfigApplicationContext等。BeanFactory爲Spring管理Bean提供了一套通用的規範。接口中提供的一些方法以下:
boolean containsBean(String beanName)Object getBean(String)Object getBean(String, Class)Class getType(String name)boolean isSingleton(String)String[] getAliases(String name)
經過這些方法,能夠方便地獲取bean,對Bean進行操做和判斷。
首先,咱們要弄明白一點,這個問題是說,怎麼把一個對象交給Spring管理,「對象」要劃重點,咱們一般採用的註解如@Compent或者XML配置這種相似的操做並不能將一個對象交給Spring管理,而是讓Spring根據咱們的配置信息及類信息建立並管理了這個對象,造成了Spring中一個Bean。把一個對象交給Spring管理主要有兩種方式
在本文中咱們完成了對FactoryBean的學習,最重要的是咱們須要明白一點,FactoryBean是Spring中特殊的一個Bean,Spring利用它提供了另外一種建立Bean的方式,FactoryBean總體的體系比較複雜,FactoryBean是如何建立一個Bean的一些細節咱們尚未涉及到,不過不要急,在源碼學習階段咱們還會接觸到它,並會對其的整個流程作進一步的分析。目前容器的擴展點咱們還剩最後一個部分,即BeanPostProcessor。BeanPostProcessor貫穿了整個Bean的生命週期,學習的難度更大。但願你們跟我一步步走下去,認認真真學習完Spring,加油!