在上篇文章中咱們已經對容器的第一個擴展點(
BeanFactoryPostProcessor
)作了一系列的介紹。其中主要介紹了Spring容器中BeanFactoryPostProcessor
的執行流程。已經Spring自身利用了BeanFactoryPostProcessor
完成了什麼功能,對於一些細節問題可能說的不夠仔細,可是在當前階段我想要作的主要是爲咱們之後學習源碼打下基礎。因此對於這些問題咱們暫且不去過多糾結,待到源碼學習階段咱們會進行更加細緻的分析。程序員在本篇文章中,咱們將要學習的是容器的另外一個擴展點(
FactoryBean
),對於FactoryBean
官網上的介紹甚短,可是若是咱們對Spring的源碼有必定了解,能夠發現Spring在不少地方都對這種特殊的Bean作了處理。話很少說,咱們開始進入正文。web
一、FactoryBean跟BeanFactory的區別面試
二、如何把一個對象交給Spring管理緩存
官網介紹
從上面這段文字咱們能夠得出如下幾個信息:微信
-
FactoryBean
主要用來定製化Bean的建立邏輯 -
當咱們實例化一個Bean的邏輯很複雜的時候,使用 FactoryBean
是很必要的,這樣能夠規避咱們去使用冗長的XML配置 -
FactoryBean
接口提供瞭如下三個方法:
-
Object getObject()
: 返回這個FactoryBean
所建立的對象。 -
boolean isSingleton()
: 返回FactoryBean
所建立的對象是否爲單例,默認返回true。 -
Class getObjectType()
: 返回這個FactoryBean
所建立的對象的類型,若是咱們能確認返回對象的類型的話,咱們應該正常對這個方法作出實現,而不是返回null。
-
Spring自身大量使用了 FactoryBean
這個概念,至少有50個FactoryBean
的實現類存在於Spring容器中 -
假設咱們定義了一個 FactoryBean
,名爲myFactoryBean
,當咱們調用getBean("myFactoryBean")
方法時返回的並非這個FactoryBean
,而是這個FactoryBean
所建立的Bean,若是咱們想獲取到這個FactoryBean
須要在名字前面拼接"&",行如這種形式:getBean("&myFactoryBean")
上面這些概念可能剛剛說的時候你們不是很明白,下面咱們經過FactoryBean
的一些應用來進一步體會這個接口的做用。mybatis
FactoryBean的應用
咱們來看下面這個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"));
}
}
運行後結果以下:ide
執行了一段複雜的建立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
自己。學習
FactoryBean相關源碼分析
咱們先看下下面這張圖:
涉及到FactoryBean
主要在3-11-6
這一步中,咱們主要關注下面這段代碼:
// .....省略無關代碼.......
// 1.判斷是否是一個FactoryBean
if (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);
}
// ...省略無關代碼.....
咱們按照順序一步步分析,首先看第一步:
-
判斷是否是一個 FactoryBean
,對應源碼以下:
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));
}
-
若是是一個 FactoryBean
那麼在getBean
時,添加前綴「&」,獲取這個FactoryBean
-
判斷是不是一個 SmartFactoryBean
,而且不是懶加載的
這裏涉及到一個概念,就是SmartFactoryBean
,實際上這個接口繼承了FactoryBean
接口,而且SmartFactoryBean
是FactoryBean
的惟一子接口,它擴展了FactoryBean
多提供了兩個方法以下:
// 是否爲原型,默認不是原型
default boolean isPrototype() {
return false;
}
// 是否爲懶加載,默認爲懶加載
default boolean isEagerInit() {
return false;
}
從上面的代碼中能夠看出,咱們噹噹實現一個FactoryBean
接口,Spring並不會在啓動時就將這個FactoryBean
所建立的Bean建立出來,爲了不這種狀況,咱們有兩種辦法:
-
實現 SmartFactoryBean
,並重寫isEagerInit
方法,將返回值設置爲true -
咱們也能夠在一個不是懶加載的Bean中注入這個 FactoryBean
所建立的Bean,Spring在解決依賴關係也會幫咱們將這個Bean建立出來
實際上咱們能夠發現,當咱們僅僅實現FactoryBean
時,其getObject()
方法所產生的Bean,咱們能夠當前是懶加載的。
-
若是是一個 SmartFactoryBean
而且不是懶加載的,那麼建立這個FactoryBean
建立的Bean。這裏須要注意的是此時建立的不是這個FactoryBean
,覺得在getBean
時並無加一個前綴「&」,因此獲取到的是其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);
}
-
獲取bean名稱
protected String transformedBeanName(String name) {
// 這個方法主要用來解析別名,若是是別名的話,獲取真實的BeanName
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// 處理FactoryBean
public 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;
});
}
-
若是是一個 FactoryBean
,將會調用其getObject()
方法,若是不是直接返回。
咱們能夠看到,在調用getObjectForBeanInstance(sharedInstance, name, beanName, null);
傳入了一個參數---name,也就是尚未通過transformedBeanName
方法處理的bean的名稱,可能會帶有「&」符號,Spring經過這個參數判斷這個Bean是否是一個FactoryBean
,若是是的話,會調用其getObject()
建立Bean。**被建立的Bean不會存放於單例池中,而是放在一個名爲factoryBeanObjectCache
的緩存中。**具體的代碼由於比較複雜,在這裏咱們就暫且不分析了,你們能夠先留個印象,源碼階段我會作詳細的分析。
Spring中FactoryBean概念的彙總(純粹我的觀點)
除了咱們在上文中說到的實現了FactoryBean
或者SmartFactoryBean
接口的Bean能夠稱之爲一個」FactoryBean
「,不知道你們對BeanDefinition
中的一個屬性是否還有印象。BeanDefinition
有屬性以下(實際上這個屬性存在於AbstractBeanDefinition
中):
@Nullable
private String factoryBeanName;
@Nullable
private String factoryMethodName;
對於這個屬性跟咱們這篇文章中介紹的FactoryBean
有什麼關係呢?
首先,咱們看看什麼狀況下bd
中會存在這個屬性,主要分爲如下兩種狀況:
第一種狀況:
@Configuration
public 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相關常見的面試題
一、FactoryBean跟BeanFactory的區別
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管理
首先,咱們要弄明白一點,這個問題是說,怎麼把一個對象交給Spring管理,「對象」要劃重點,咱們一般採用的註解如@Compent
或者XML配置這種相似的操做並不能將一個對象交給Spring管理,而是讓Spring根據咱們的配置信息及類信息建立並管理了這個對象,造成了Spring中一個Bean。把一個對象交給Spring管理主要有兩種方式
-
就是用咱們這篇文章中的主角, FactoryBean
,咱們直接在FactoryBean
的getObject
方法直接返回須要被管理的對象便可 -
@Bean
註解,一樣經過@Bean
註解標註的方法直接返回須要被管理的對象便可。
總結
在本文中咱們完成了對FactoryBean
的學習,最重要的是咱們須要明白一點,FactoryBean
是Spring中特殊的一個Bean,Spring利用它提供了另外一種建立Bean的方式,FactoryBean
總體的體系比較複雜,FactoryBean
是如何建立一個Bean的一些細節咱們尚未涉及到,不過不要急,在源碼學習階段咱們還會接觸到它,並會對其的整個流程作進一步的分析。目前容器的擴展點咱們還剩最後一個部分,即BeanPostProcessor
。BeanPostProcessor
貫穿了整個Bean的生命週期,學習的難度更大。但願你們跟我一步步走下去,認認真真學習完Spring,加油!
本文分享自微信公衆號 - 程序員DMZ(programerDmz)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。