前言html
前兩篇文章介紹瞭如何使用
@Component
,@Import
註解來向spring容器中註冊組件(javaBean),本文將介紹經過FactoryBean
接口繼續向spring容器中註冊組件。可能第一印象是spring中BeanFactory
接口,可是這裏確實說的是FactoryBean
。java
推薦閱讀spring
根據接口名稱,咱們也能夠簡單看出二者的區別緩存
接下來將進入本文正題,如何經過FactoryBean
接口向spring容器中註冊組件app
正如前面說的,FactoryBean也是spring中的一個Bean,可是它又是一個特殊的Bean,它的存在是爲了生產其餘的JavaBean。首先咱們看看FactoryBean
自身的接口定義ide
public interface FactoryBean<T> { /** * 從Spring容器中獲取Bean時會調用此方法,返回一個T對象 */ @Nullable T getObject() throws Exception; /** * 此工廠Bean返回對象的類型 */ @Nullable Class<?> getObjectType(); /** * 工廠Bean建立的對象是否爲單例, * 若是返回false,說明getObject方法的實例對象不是單例的, * Spring每次從容器中獲取T對象時,都調用getObject方法建立一個對象 */ default boolean isSingleton() { //spring 5 接口默認返回true(單例) return true; } }
FactoryBean
接口定義簡單明瞭,就是用來獲取一個Bean的基本信息,下面咱們本身實現該接口,來生產一個javaBeanpost
/** * 產生 Bike 對象的工廠Bean */ @Component public class BikeFactoryBean implements FactoryBean<Bike> { public Bike getObject() throws Exception { System.out.println("......開始建立Bike對象......"); return new Bike(); } public Class<?> getObjectType() { return Bike.class; } public boolean isSingleton() { return true; } }
自定義的一個JavaBean類學習
/** * 待註冊的自定義組件 */ @Data public class Bike { private String id = "by FactoryBean"; }
添加spring容器啓動的引導類測試
/** * spring 容器啓動引導類,測試 FactoryBean 功能 */ @ComponentScan("com.spring.study.ioc.factorybean") public class TestFactoryBeanBootstrap { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestFactoryBeanBootstrap.class); //獲取工廠Bean自己的Id String[] beanNames = applicationContext.getBeanNamesForType(BikeFactoryBean.class); System.out.println("BikeFactoryBean names:" + Arrays.asList(beanNames)); //獲取工廠Bean產生的Bean的Id beanNames = applicationContext.getBeanNamesForType(Bike.class); System.out.println("Bike bean names:" + Arrays.asList(beanNames)); Object bean = applicationContext.getBean("bikeFactoryBean"); System.out.println(bean); bean = applicationContext.getBean(Bike.class); System.out.println(bean); // 獲取工廠Bean 自己的實例對象 bean = applicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX + "bikeFactoryBean"); System.out.println(bean); applicationContext.close(); } }
啓動spring容器,控制檯打印結果:ui
BikeFactoryBean names:[&bikeFactoryBean]
Bike bean names:[bikeFactoryBean]
......開始建立Bike對象......
Bike(id=by FactoryBean)
Bike(id=by FactoryBean)
com.spring.study.ioc.factorybean.BikeFactoryBean@4eb7f003
由結果能夠看出
BikeFactoryBean
類上加了@Component
註解,可是從spring容器仍然能夠獲取到Bike
類的信息&
符,也就是說,工廠Bean定義的Id實際爲getObject()方法返回Bean的Id,而工廠Bean自己的Id被添加了一個前綴&
符&
符,而此前綴在BeanFactory
接口中已經定義了FACTORY_BEAN_PREFIX
若是將BikeFactoryBean
的isSingleton()
方法返回了false
public boolean isSingleton() { return false; }
從新啓動spring容器,能夠看以下結果:
BikeFactoryBean names:[&bikeFactoryBean]
Bike bean names:[bikeFactoryBean]
......開始建立Bike對象......
Bike(id=by FactoryBean)
......開始建立Bike對象......
Bike(id=by FactoryBean)
com.spring.study.ioc.factorybean.BikeFactoryBean@4eb7f003
由結果能夠看出,惟一的變化在於從spring容器中屢次獲取實際Bean時,工廠Bean的getObject()方法被屢次進行了調用。這與spring容器中被標識爲原型的普通Bean相同,每次從spring中獲取Bean時都會被實例化。
要想了解FactoryBean的執行過程,就須要結合Spring容器啓動的過程來進行分析。而spring容器的啓動過程通過了紛繁複雜的步驟。爲了儘量少的入坑和挖坑,下面僅結合FactoryBean相關的源碼進行說明。
在開始入坑之旅以前,結合前面的例子作幾點說明,方便後續講解
前面定義的 BikeFactoryBean 類上面直接添加了@Component註解,這樣spring會默認以類名首字母小寫(bikeFactoryBean)做爲beanName;若是使用@Bean進行註冊時,spring默認會以方法名做爲beanName,下面繼續以「BikeFactoryBean」爲例。
BeanFactoryPostProcessor
,BeanPostProcessor
以及註冊Listener
後會執行org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization()
方法,此方法會對剩下全部的非Abstract
、非LazyInit
的單實例Bean進行實例化,如下爲部分代碼片斷。@Override public void preInstantiateSingletons() throws BeansException { ...省略代碼... // 1. 拷貝一份副本:spring容器中的全部的Bean名稱 List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // 2. 遍歷每個beanName,嘗試經過getBean()方法進行實例化 // 在getBean()方法內部會先嚐試從容器singletonObjects中獲取Bean,若是沒有才會進行實例化操做 for (String beanName : beanNames) { // 3. 經過beanName獲取Bean定義信息 BeanDefinition RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 4. 根據BeanDefinition判斷該Bean是否不是抽象的,單例的,非懶加載的 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 5. 知足上面的條件後,在根據beanName判斷此Bean是不是一個工廠Bean(實現了FactoryBean接口) if (isFactoryBean(beanName)) { // 6. 若是是一個工廠Bean,則在此處進行工廠Bean自己的實例化 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); ...省略代碼... } else { // 若是不是工廠bean,也是調用getBean()方法進行實例化 getBean(beanName); } } } ...省略代碼... }
preInstantiateSingletons()是在finishBeanFactoryInitialization()方法內部調用的
根據上面的代碼流程,在第6步時,會對
BikeFactoryBean
類自己進行實例化,而且能夠看出傳遞的beanName爲初始註冊的name前添加了&
符前綴即&bikeFactoryBean
,用於在genBean()方法內部標識它是一個工廠Bean。可是在跟蹤源碼後發現,在getBean()方法內部,會先將傳入的beanName(&bikeFactoryBean
)開頭的&
符去除,而且最終實例化Bean後,在容器中保存的beanName仍是不帶&
符前綴的名稱即bikeFactoryBean
Bike
類的beanName
和Bean實例時,又是怎麼獲取到的呢?帶着疑問,咱們繼續看後面的代碼。首先,上面的例子中調用了applicationContext.getBeanNamesForType(Bike.class)
方法來獲取Bike
類的beanName
。因此繼續跟蹤此方法看看到底發生了什麼。// getBeanNamesForType()方法內部最終調用了此方法,可斷點跟蹤至此 private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { // 用來保存符合條件的結果(beanName集合) List<String> result = new ArrayList<>(); // 與上面的代碼類似,遍歷spring容器中註冊的全部的beanNames for (String beanName : this.beanDefinitionNames) { if (!isAlias(beanName)) { try { // 根據beanName獲取Bean的定義信息 BeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // 根據BeanDefinition 進行檢查 if (!mbd.isAbstract() && (allowEagerInit || (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) && !requiresEagerInitForType(mbd.getFactoryBeanName()))) { // 根據beanName和Bean的定義信息判斷是不是工廠Bean boolean isFactoryBean = isFactoryBean(beanName, mbd); BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); boolean matchFound = (allowEagerInit || !isFactoryBean || (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) && (includeNonSingletons || (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) && // 根據Bean的定義信息判斷完後,在此方法中判斷此beanName對應的Bean實例是否與傳入的類型相匹配 isTypeMatch(beanName, type); // 若是根據beanName得到的是一個工廠Bean,而且與傳入的類型不匹配,則知足條件,將beanName添加 & 符前綴 if (!matchFound && isFactoryBean) { // 對於工廠Bean,接下來嘗試匹配工廠Bean實例自己 beanName = FACTORY_BEAN_PREFIX + beanName; matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type); } // 若是獲取的Bean實例與傳入的類型匹配,將beanName添加到結果集合中 if (matchFound) { result.add(beanName); } } } catch (CannotLoadBeanClassException ex) { // ...省略代碼... } catch (BeanDefinitionStoreException ex) { // ...省略代碼... } } } // ...省略代碼... return StringUtils.toStringArray(result); }
isTypeMatch()方法中的部分代碼
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { // 對beanName進行處理,將開頭的 & 符過濾 String beanName = transformedBeanName(name); // 從spring容器中獲取單實例Bean,因爲spring容器啓動時已經將單實例Bean進行了實例化, // 因此此時能夠直接在容器中獲得Bean實例 Object beanInstance = getSingleton(beanName, false); if (beanInstance != null && beanInstance.getClass() != NullBean.class) { // 獲取到Bean實例後,判斷是否爲工廠Bean if (beanInstance instanceof FactoryBean) { // 若是是工廠Bean,而且獲取的beanName不是以&符開頭 if (!BeanFactoryUtils.isFactoryDereference(name)) { // 將實例強轉爲 FactoryBean 並調用 FactoryBean接口的getObjectType()方法, // 獲取工廠Bean所生產的實例類型 Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance); // 判斷工廠Bean生產的實例類型與傳入的類型是否匹配 return (type != null && typeToMatch.isAssignableFrom(type)); } else { return typeToMatch.isInstance(beanInstance); } } // ...省略代碼... } // ...省略代碼... }
getTypeForFactoryBean()方法中的代碼
protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) { try { if (System.getSecurityManager() != null) { return AccessController.doPrivileged((PrivilegedAction<Class<?>>) factoryBean::getObjectType, getAccessControlContext()); } else { // 直接調用 FactoryBean 接口的 getObjectType()方法,獲取生產的類型 return factoryBean.getObjectType(); } } catch (Throwable ex) { // Thrown from the FactoryBean's getObjectType implementation. logger.info("FactoryBean threw exception from getObjectType, despite the contract saying " + "that it should return null if the type of its object cannot be determined yet", ex); return null; } }
以上即是getBeanNamesForType()
方法通過的部分重要代碼
由此能夠看出,當咱們想要獲取
BikeFactoryBean
自己的beanName時,doGetBeanNamesForType方法內部將bikeFactoryBean
前添加了&
符前綴,因而便獲取到了&bikeFactoryBean
;
當咱們想要獲取Bike類型的beanName時,spring會經過容器遍歷已經註冊的全部的beanNames,而後根據beanName及對應的Bean定義信息BeanDefinition進行判斷過濾,而且對於全部的工廠Bean,會獲取spring容器中已經實例化的Bean對象,調用 FactoryBean 接口的 getObjectType()方法,獲得工廠Bean所生產的實例類型,而後與Bike.class相比較,若是匹配,則將此beanName保存到結果集中,最後返回。因此,當咱們想要獲取Bike類型的beanName時,從spring容器中即可以找到bikeFactoryBean
。
beanName
後,咱們繼續獲取Bike實例
beanName
後再最終調用doGetBean
方法獲取實例對象。下面看看部分源碼protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { // 對傳入的beanName進行過濾,去除&符前綴 final String beanName = transformedBeanName(name); Object bean; // 從spring容器中獲取實例,因爲spring容器啓動時已經將單實例Bean進行實例化,因此此時能夠直接得到 Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } // 獲取指定的Bean實例,若是是工廠bean,則爲Bean實例自己或其建立的對象。 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } // ...省略代碼... return (T) bean; }
從上面的代碼能夠看出,獲取Bike實例的具體代碼還在getObjectForBeanInstanc()
方法內部,咱們繼續查看
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // 判斷beanName是不是以&符開頭的 if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } } // 根據beanName從spring容器中獲取的Bean實例若是不是工廠Bean,或者beanName是以&符開頭,就直接返回這個Bean實例 // 當咱們獲取Bike類型的實例時,beanName爲「bikeFactoryBean」, // beanInstance爲「BikeFactoryBean」類型,是一個工廠Bean,因此條件不知足,繼續向下走 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object = null; if (mbd == null) { // 根據beanName從緩存中獲取Bean實例,第一次來獲取Bike實例時爲空, // factoryBeanObjectCache.get(beanName); // 後續再獲取時,即可以在此得到到,而後返回 object = getCachedObjectForFactoryBean(beanName); } if (object == null) { // 將獲取的工廠Bean強轉爲 FactoryBean 類型,以便下面調用其getObject()方法獲取對象 FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // 獲取bean定義信息 if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); //在此方法內部調用 FactoryBean 接口的 getObject()方法獲取對象 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
距離真相還差兩步了,堅持就是勝利,咱們繼續看getObjectFromFactoryBean()
的源碼
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { // 此處調用 FactoryBean 的isSingleton()方法,判斷是不是一個單列 // 若是是單例的,走if內部,獲取到對象後,會保存到factoryBeanObjectCache緩存中,以便後續使用 if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { // 檢查緩存中是否已經存在 Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { // 調用最後一個方法,執行FactoryBean 的 getObject()方法獲取對象 object = doGetObjectFromFactoryBean(factory, beanName); // 再次檢查緩存 Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { // ... 省略代碼 ... if (containsSingleton(beanName)) { // 將獲取的對象放入factoryBeanObjectCache緩存中,以便後續使用 this.factoryBeanObjectCache.put(beanName, object); } } } return object; } } // 若是不是單例的,每次獲取的對象直接返回,不會放入緩存中,因此每次都會調用getObject()方法 else { Object object = doGetObjectFromFactoryBean(factory, beanName); if (shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; } }
根據上面的流程,終於來到了最後一步
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 直接調用 FactoryBean 接口的 getObject()方法獲取實例對象 object = factory.getObject(); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } // ... 省略代碼 ... return object; }
通過如此多的代碼,spring終於幫咱們獲取到Bike對象實例
經過BikeFactoryBean來獲取Bike類的實例時,spring先獲取Bike類型對應的beanName(bikeFactoryBean),而後根據beanName獲取到工廠Bean實例自己(BikeFactoryBean),最終spring會調用BikeFactoryBean 的 getObject()方法來獲取Bike對象實例。而且根據 BikeFactoryBean 實例的 isSingleton() 方法來判斷Bike類型的實例是否時單例的,依此來決定要不要將獲取的Bike對象放入到緩存中,以便後續使用。
本文主要講解了如何經過 FactoryBean
接口向spring容器中注入組件,經過簡單的案例進行模擬,並根據案例對源碼的執行過程進行跟蹤,分析了FactoryBean
接口的執行過程。
另外,在每一次跟蹤spring源碼時,都會有新的收穫。在spring龐大的體系下,只有定位好本身的目標,明確本身的需求,纔不會被spring無限的代碼所淹沒。
學習永遠都不是一件簡單的事情,能夠有迷茫,能夠懶惰,可是前進的腳步永遠都不能中止。
不積跬步,無以致千里;不積小流,無以成江海;