為什么大多數(shù)IOC容器使用ApplicationContext,而不用BeanFactory

Spring框架附帶了兩個IOC容器–?BeanFactory?和?ApplicationContext.?BeanFactory是IOC容器的最基本版本,ApplicationContext擴展了BeanFactory的功能。那么本篇文章中,我們將通過實際例子了解這兩個IOC容器之間的顯著差異。
2. 延遲加載 vs. 預加載
BeanFactory 按需加載bean,而 ApplicationContext 則在啟動時加載所有bean。因此,BeanFactory與ApplicationContext相比是輕量級的。讓我們用一個例子來理解它。
2.1.?BeanFactory?延遲加載
假設我們有一個名為?Student?單例Bean:
public?class?Student?{
????public?static?boolean?isBeanInstantiated?=?false;
?
????public?void?postConstruct()?{
????????setBeanInstantiated(true);
????}
?
????//standard?setters?and?getters
}
我們將把?postConstruct()?方法定義為BeanFactory配置文件?ioc-container-difference-example.xml?中的?init method:
<bean?id="student"?class="com.baeldung.ioccontainer.bean.Student"?init-method="postConstruct"/>
現(xiàn)在,讓我們編寫一個測試用例來創(chuàng)建一個BeanFactory?來檢查它是否加載了Student?bean:
@Test
public?void?whenBFInitialized_thenStudentNotInitialized()?{
????Resource?res?=?new?ClassPathResource("ioc-container-difference-example.xml");
????BeanFactory?factory?=?new?XmlBeanFactory(res);
????
????assertFalse(Student.isBeanInstantiated());
}
這里,沒有初始化 Student 對象。換句話說,只有 BeanFactory 被初始化了。只有當我們顯式調(diào)用*getBean()方法時,*BeanFactory 中定義的?bean?才會被加載。讓我們檢查一下?Student?bean 的初始化情況,我們手動調(diào)用?getBean()?方法:
@Test
public?void?whenBFInitialized_thenStudentInitialized()?{
????Resource?res?=?new?ClassPathResource("ioc-container-difference-example.xml");
????BeanFactory?factory?=?new?XmlBeanFactory(res);
????Student?student?=?(Student)?factory.getBean("student");
?
????assertTrue(Student.isBeanInstantiated());
}
這里,Student?bean 成功加載。因此,BeanFactory?只在需要時加載bean。
2.2.?ApplicationContext?預加載
現(xiàn)在,讓我們用ApplicationContext代替BeanFactory?我們只定義*ApplicationContext,*它將使用預加載策略立即加載所有bean:
@Test
public?void?whenAppContInitialized_thenStudentInitialized()?{
????ApplicationContext?context?=?new?ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
????
????assertTrue(Student.isBeanInstantiated());
}
在這里,即使我們沒有調(diào)用 getBean() 方法,也會創(chuàng)建 Student 對象?ApplicationContext?被認為是一個沉重的IOC容器,因為它的預加載策略在啟動時加載所有bean。相比之下,BeanFactory?是輕量級的,在內(nèi)存受限的系統(tǒng)中非常方便。盡管如此,大多數(shù)用例仍然首選使用 ApplicationContext,這是為什么呢?
3. 企業(yè)應用程序功能
ApplicationContext?以更面向框架的風格增強了BeanFactory,并提供了一些適用于企業(yè)應用程序的功能。
例如,它提供了消息傳遞(i18n或國際化)功能、事件發(fā)布功能、基于注釋的依賴注入,以及與Spring AOP特性的簡單集成。
除此之外,ApplicationContext幾乎支持所有類型的 bean 作用域,但是BeanFactory只支持兩個作用域——Singleton和Prototype。因此,在構(gòu)建復雜的企業(yè)應用程序時,最好使用ApplicationContext。
4. 自動注冊BeanFactoryPostProcessor和BeanPostProcessor
**ApplicationContext 在啟動時自動注冊 BeanFactoryPostProcessor 和 BeanPostProcessor **。然而,BeanFactory不會自動注冊這些接口。
4.1. 在 BeanFactory 中注冊
為了理解,讓我們寫兩個類。首先,我們有CustomBeanFactoryPostProcessor類,它實現(xiàn)了BeanFactoryPostProcessor:
public?class?CustomBeanFactoryPostProcessor?implements?BeanFactoryPostProcessor?{
????private?static?boolean?isBeanFactoryPostProcessorRegistered?=?false;
????
????@Override
????public?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory){
????????setBeanFactoryPostProcessorRegistered(true);
????}
?
????//?standard?setters?and?getters
}
這里,我們重寫了?postProcessBeanFactory()?方法來檢查它的注冊。其次,我們還有另一個類,CustomBeanPostProcessor,它實現(xiàn)了BeanPostProcessor:
public?class?CustomBeanPostProcessor?implements?BeanPostProcessor?{
????private?static?boolean?isBeanPostProcessorRegistered?=?false;
????
????@Override
????public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName){
????????setBeanPostProcessorRegistered(true);
????????return?bean;
????}
?
????//standard?setters?and?getters
}
這里,我們重寫了?PostProcessBeforeAlization()?方法來檢查其注冊。另外,我們在?ioc-container-difference-example.xml?配置文件中配置了這兩個類:
<bean?id="customBeanPostProcessor"?
??class="com.baeldung.ioccontainer.bean.CustomBeanPostProcessor"?/>
<bean?id="customBeanFactoryPostProcessor"?
??class="com.baeldung.ioccontainer.bean.CustomBeanFactoryPostProcessor"?/>
讓我們看一個測試用例來檢查這兩個類是否在啟動期間自動注冊:
@Test
public?void?whenBFInitialized_thenBFPProcessorAndBPProcessorNotRegAutomatically()?{
????Resource?res?=?new?ClassPathResource("ioc-container-difference-example.xml");
????ConfigurableListableBeanFactory?factory?=?new?XmlBeanFactory(res);
?
????assertFalse(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
????assertFalse(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}
從我們的測試中我們可以看到,自動注冊并沒有發(fā)生。?現(xiàn)在,讓我們看看一個測試用例,手動將它們添加到 BeanFactory:
@Test
public?void?whenBFPostProcessorAndBPProcessorRegisteredManually_thenReturnTrue()?{
????Resource?res?=?new?ClassPathResource("ioc-container-difference-example.xml");
????ConfigurableListableBeanFactory?factory?=?new?XmlBeanFactory(res);
?
????CustomBeanFactoryPostProcessor?beanFactoryPostProcessor?
??????=?new?CustomBeanFactoryPostProcessor();
????beanFactoryPostProcessor.postProcessBeanFactory(factory);
????assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
?
????CustomBeanPostProcessor?beanPostProcessor?=?new?CustomBeanPostProcessor();
????factory.addBeanPostProcessor(beanPostProcessor);
????Student?student?=?(Student)?factory.getBean("student");
????assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}
這里,我們使用?postProcessBeanFactory()?方法注冊?CustomBeanFactoryPostProcessor,使用?addBeanPostProcessor()?方法注冊CustomBeanPostProcessor。在這種情況下,它們都注冊成功。
4.2. 在?ApplicationContext?中注冊
如前所述,ApplicationContext會自動注冊這兩個類,而無需編寫額外的代碼。讓我們在單元測試中驗證此行為:
@Test
public?void?whenAppContInitialized_thenBFPostProcessorAndBPostProcessorRegisteredAutomatically()?{
????ApplicationContext?context?
??????=?new?ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
?
????assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered());
????assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered());
}
我們可以看到,這兩個類的自動注冊都是成功的。因此,建議使用ApplicationContext,因為Spring2.0(及更高版本)大量使用BeanPostProcessor。還有一點值得注意的是如果使用的是普通的 BeanFactory,那么事務和AOP之類的功能將不會生效(除非你編寫額外的代碼實現(xiàn),那就另當別論了)。這樣可能會導致代碼很混亂,因為配置看起來貌似沒毛病。
5. 寫在結(jié)尾
ApplicationContext?提供了一些高級功能,包括一些面向企業(yè)應用程序的功能,而BeanFactory只提供了基本功能。因此,一般建議使用?ApplicationContext?,只有在內(nèi)存消耗非常關(guān)鍵的情況下,我們才應該考慮去使用BeanFactory。
往期推薦
﹀
﹀
﹀
深度內(nèi)容
推薦加入
阿里云全線產(chǎn)品超低價,點擊“閱讀原文”進入




