Spring中FactoryBean的作用和實現原理

NO IMAGE

BeanFactory與FactoryBean,相信很多剛翻看Spring源碼的同學跟我一樣很好奇這倆貨怎麼長得這麼像,分別都是幹啥用的。BeanFactory是Spring中Bean工廠的頂層接口,也是我們常說的SpringIOC容器,它定下了IOC容器的一些規範和常用方法並管理著Spring中所有的Bean,今天我們不講它,我們看一下後面那個FactoryBean。

先說下FactoryBean和其作用再開始分析:首先它是一個Bean,但又不僅僅是一個Bean。它是一個能生產或修飾對象生成的工廠Bean,類似於設計模式中的工廠模式和裝飾器模式。它能在需要的時候生產一個對象,且不僅僅限於它自身,它能返回任何Bean的實例。


上面的解釋有點抽象,那麼我們瀏覽一遍FactoryBean在Spring中是怎麼應用的就好懂了。我們都知道在Spring中我們的Bean都會被Spring的IOC容器所管理,在AbstractApplicationContext中有一個很重要的方法:refresh(),項目啟動或重啟的時候refresh()會調用getBean()初始化所有的Bean,這個getBean()最終會指向AbstractBeanFactory中的getBean()方法。

在AbstractBeanFactory中,不管是根據名稱還是根據類型,getBean()最終都會調用doGetBean(),在doGetBean()方法中一開始就獲取了名稱beanName和實例sharedInstance,這個方法太長,這裡就貼前面兩行。

String beanName = this.transformedBeanName(name);
Object sharedInstance = this.getSingleton(beanName);

transformedBeanName(name)是為了獲取Bean真正的名稱,它會去掉name前面的'&',而getSingleton(beanName)是從父類容器singletonObjects中取的這個Bean的實例。在Spring中還有很多這樣的容器,比如DefaultListableBeanFactory中的beanDefinitionMap,它就是的IOC容器真正保存Bean的地方,它是一個HashMap。類似的還有FactoryBeanRegistrySupport中的factoryBeanObjectCache等。

回到doGetBean()方法中,拿到sharedInstance後,後面的一大堆操作做了單例、多例等判斷,最終會走到this.getObjectForBeanInstance(),關鍵部分就在這個方法中,進入方法代碼。

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, 
@Nullable RootBeanDefinition mbd) {
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(this.transformedBeanName(name), beanInstance.getClass());
}
}
if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
Object object = null;
if (mbd == null) {
object = this.getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
FactoryBean<?> factory = (FactoryBean)beanInstance;
if (mbd == null && this.containsBeanDefinition(beanName)) {
mbd = this.getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = mbd != null && mbd.isSynthetic();
object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
} else {
return beanInstance;
}
}

在上面的代碼中有兩個判斷分別是beanInstance instanceof FactoryBeanBeanFactoryUtils.isFactoryDereference(name),前面判斷的是beanInstance是否屬於FactoryBean或其子類的實例,後面這個方法判斷name是否不為空且以&開頭。

    public static boolean isFactoryDereference(@Nullable String name) {
return name != null && name.startsWith("&");
}

如果beanInstance不屬於FactoryBean或其子類的實例,或者name是以&開頭就直接返回實例對象beanInstance,否則進入到if分支中。在if分支裡的各種if .. ==null判斷是為了提高性能,咱們只挑關鍵部分看:object = this.getObjectFromFactoryBean(factory, beanName, !synthetic); 繼續跟蹤進去看代碼。

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && this.containsSingleton(beanName)) {
synchronized(this.getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = this.doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
} else {
if (shouldPostProcess) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
return object;
}
this.beforeSingletonCreation(beanName);
try {
object = this.postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable var14) {
throw new BeanCreationException(beanName, 
"Post-processing of FactoryBean's singleton object failed", var14);
} finally {
this.afterSingletonCreation(beanName);
}
}
if (this.containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
} else {
Object object = this.doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = this.postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable var17) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", var17);
}
}
return object;
}
}

這裡面也是做了很多判斷,咱們也只挑關鍵部分看。這裡說一下個人想法,**看源碼的時候如果我們一直追究所有的細節那會讓我們會越陷越深,掉入細節的無底洞,稍不留神腦回路跟不上就會蒙圈。我們要學會找源碼中的關鍵部分看,弄懂主要流程和本次看源碼的目的的那部分就行。等我們對Spring整體有了一個很好的理解之後,再回頭看之前不懂的代碼就會豁然開朗。**在上面這個方法中不管是走上面的if分支還是到下邊的else中,關鍵部分就是object = this.doGetObjectFromFactoryBean(factory, beanName)這段代碼調用,繼續點進去。

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = this.getAccessControlContext();
try {
object = AccessController.doPrivileged(factory::getObject, acc);
} catch (PrivilegedActionException var6) {
throw var6.getException();
}
} else {
object = factory.getObject();
}
} catch (FactoryBeanNotInitializedException var7) {
throw new BeanCurrentlyInCreationException(beanName, var7.toString());
} catch (Throwable var8) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", var8);
}
if (object == null) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName, 
"FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}

在這個方法中有一行關鍵代碼:object = factory.getObject(); 這個factory就是我們傳入的beanInstance實例。繞了這麼一大圈,getBean方法返回的居然是我們實現FactoryBean接口定義的getObject方法。

那麼跟一開始對FactoryBean的描述印證了,FactoryBean是一個能生產或修飾對象生成的工廠Bean。一個Bean如果實現了FactoryBean接口,那麼根據該Bean的名稱獲取到的實際上是getObject()返回的對象,而不是這個Bean自身實例,如果要獲取這個Bean自身實例,那麼需要在名稱前面加上’&’符號

一般情況下,Spring通過反射機制利用的class屬性指定實現類實例化Bean,在某些情況下,實例化Bean過程比較複雜,如果按照傳統的方式,則需要在中提供大量的配置信息。配置方式的靈活性是受限的,這時採用編碼的方式可能會得到一個簡單的方案。Spring為此提供了一個org.springframework.bean.factory.FactoryBean的工廠類接口,用戶可以通過實現該接口定製實例化Bean的邏輯。FactoryBean接口對於Spring框架來說佔用重要的地位,Spring自身就提供了70多個FactoryBean的實現。它們隱藏了實例化一些複雜Bean的細節,給上層應用帶來了便利。從Spring3.0開始,FactoryBean開始支持泛型,即接口聲明改為FactoryBean的形式


首發地址:www.guitu18.com/post/2019/0…

原理弄明白了,下面通過代碼測試驗證上面的流程,先定義一個Bean實現FactoryBean接口。

@Component
public class MyBean implements FactoryBean {
private String message;
public MyBean() {
this.message = "通過構造方法初始化實例";
}
@Override
public Object getObject() throws Exception {
MyBean myBean = new MyBean();
myBean.message = "通過FactoryBean.getObject()創建實例";
// 這裡並不一定要返回MyBean自身的實例,可以是其他任何對象的實例
return myBean;
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
public String getMessage() {
return message;
}
}

MyBean實現了FactoryBean接口的兩個方法,getObject()是可以返回任何對象的實例的,這裡測試就返回MyBean自身實例,且返回前給message字段賦值。同時在構造方法中也為message賦值。然後測試代碼中先通過名稱獲取Bean實例,打印message的內容,再通過’&’+名稱獲取實例並打印message內容。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest {
@Autowired
private ApplicationContext context;
@Test
public void test() {
MyBean myBean1 = (MyBean) context.getBean("myBean");
System.out.println("myBean1 = " + myBean1.getMessage());
MyBean myBean2 = (MyBean) context.getBean("&myBean");
System.out.println("myBean2 = " + myBean2.getMessage());
System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
}
}
myBean1 = 通過FactoryBean.getObject()初始化實例
myBean2 = 通過構造方法初始化實例
myBean1.equals(myBean2) = false

通過測試我們發現獲取的兩個實例中的message的值不一樣,對比兩個對象的引用也不相同。上述所講關於FactoryBean的內容印證完畢,本文結束。

相關文章

FishRedux完成一個玩安卓客戶端

淺談MySQL的事務與ACID

HashMap原理技術知識整理

SpringMVC加載流程