1. 引言
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" init-method="postConstruct"/>
現在,讓我們撰寫一個測驗用例來創建一個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 被初始化了,只有當我們顯式呼叫getBean()方法時,BeanFactory 中定義的 bean 才會被加載,
讓我們檢查一下 Student bean 的初始化情況,我們手動呼叫 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 預加載
現在,讓我們用ApplicationContext代替BeanFactory
我們只定義ApplicationContext,它將使用預加載策略立即加載所有bean:
@Test
public void whenAppContInitialized_thenStudentInitialized() {
ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
assertTrue(Student.isBeanInstantiated());
}
在這里,即使我們沒有呼叫 getBean() 方法,也會創建 Student 物件
ApplicationContext 被認為是一個沉重的IOC容器,因為它的預加載策略在啟動時加載所有bean,相比之下,BeanFactory 是輕量級的,在記憶體受限的系統中非常方便,盡管如此,大多數用例仍然首選使用 ApplicationContext,這是為什么呢?
3. 企業應用程式功能
ApplicationContext 以更面向框架的風格增強了BeanFactory,并提供了一些適用于企業應用程式的功能,
例如,它提供了訊息傳遞(i18n或國際化)功能、事件發布功能、基于注釋的依賴注入,以及與Spring AOP特性的簡單集成,
除此之外,ApplicationContext幾乎支持所有型別的 bean 作用域,但是BeanFactory只支持兩個作用域——Singleton和Prototype,因此,在構建復雜的企業應用程式時,最好使用ApplicationContext,
4. 自動注冊BeanFactoryPostProcessor和BeanPostProcessor
**ApplicationContext 在啟動時自動注冊 BeanFactoryPostProcessor 和 BeanPostProcessor **,然而,BeanFactory不會自動注冊這些介面,
4.1. 在 BeanFactory 中注冊
為了理解,讓我們寫兩個類,
首先,我們有CustomBeanFactoryPostProcessor類,它實作了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,它實作了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"
/>
<bean id="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());
}
從我們的測驗中我們可以看到,自動注冊并沒有發生,
現在,讓我們看看一個測驗用例,手動將它們添加到 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之類的功能將不會生效(除非你撰寫額外的代碼實作,那就另當別論了),這樣可能會導致代碼很混亂,因為配置看起來貌似沒毛病,
5. 寫在結尾
ApplicationContext 提供了一些高級功能,包括一些面向企業應用程式的功能,而BeanFactory只提供了基本功能,因此,一般建議使用 ApplicationContext ,只有在記憶體消耗非常關鍵的情況下,我們才應該考慮去使用BeanFactory,
如果你覺得文章還不錯,記得關注公眾號: 鍋外的大佬
劉一手的博客
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/213199.html
標籤:Java
