1. 概述
Spring 框架提供了兩個依賴注入 (IOC) 容器:<em >BeanFactory</em> 和 <em >ApplicationContext</em>。<em >BeanFactory</em> 是最基礎的 IOC 容器版本,而 <em >ApplicationContext</em> 擴展了 <em >BeanFactory</em> 的功能。
在本快速教程中,我們將通過實際示例,理解這兩個 IOC 容器之間的主要差異。
2. 按需加載與即時加載
<em>BeanFactory</em> 在需要時才加載 Bean,而 <em>ApplicationContext</em> 在啓動時加載所有 Bean。因此,<em>BeanFactory</em> 比 <em>ApplicationContext</em> 輕量級。我們通過一個例子來理解這一點。
2.1. 延遲加載中使用 BeanFactory
假設我們有一個名為 Student 的單例 Bean 類,該類包含一個方法:
public class Student {
public static boolean isBeanInstantiated = false;
public void postConstruct() {
setBeanInstantiated(true);
}
//standard setters and getters
}我們將會定義 postConstruct() 方法為 init-method 的 BeanFactory 配置文件的 ioc-container-difference-example.xml 中的定義:
<bean id="student" class="com.baeldung.ioccontainer.bean.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被初始化。我們定義的BeanFactory中的Bean只有在我們顯式調用getBean()方法時才會加載。
讓我們檢查我們手動調用getBean()方法時,Student Bean 的初始化情況:
@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());
}這裏創建了 Student 對象,儘管我們尚未調用 getBean() 方法。
ApplicationContext 被認為是重型 IOC 容器,因為它採用急加載策略,在啓動時加載所有 Bean。相比之下,BeanFactory 則輕量級,在內存受限的系統中非常實用。儘管如此,在下一部分,我們將看到為什麼 ApplicationContext 在大多數用例中更受歡迎.
3. 企業應用程序功能
ApplicationContext 增強了BeanFactory,並以更具框架化的方式提供了多種功能,適用於企業應用程序。
例如,它提供消息傳遞(國際化或國際化)功能,事件發佈功能,基於註解的依賴注入,以及與 Spring AOP 功能的輕鬆集成。
此外,ApplicationContext 支持幾乎所有類型的 Bean 作用域,但BeanFactory 僅支持兩個作用域——單例和原型。因此,在構建複雜的企業應用程序時,最好始終使用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
}在這裏,我們覆蓋了 postProcessBeforeInitialization() 方法,以檢查其是否已註冊。
此外,我們還配置了 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());
}如我們所見,從我們的測試中可以看出,自動註冊並未發生。
現在,讓我們來看一個手動在 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,因為 Spring 2.0 (及更高版本) 強烈依賴 BeanPostProcessor。
此外,還值得注意的是,如果您使用的是純粹的 BeanFactory,那麼諸如事務和 AOP 等功能將不會生效 (至少在不編寫額外的代碼行的情況下)。 這可能會導致混淆,因為配置本身看起來沒有任何問題。
5. 結論
在本文中,我們探討了 <em >ApplicationContext</em >> 和 <em >BeanFactory</em >> 之間的關鍵差異,並通過實際示例進行了説明。
<em >ApplicationContext</em >> 提供了高級功能,包括針對企業應用程序的多種特性,而 <em >BeanFactory</em >> 則僅提供基本功能。因此,通常建議使用 <em >ApplicationContext</em >>,並且在內存消耗至關重要時,我們應該使用 <em >BeanFactory</em >>。