BeanFactory 和 ApplicationContext 之间的区别

发布时间 2023-06-12 11:06:21作者: 小树木
 

一、概述

 

Spring 框架带有两个 IOC 容器—— BeanFactoryApplicationContextBeanFactory是 IOC 容器的最基本版本,而 ApplicationContext扩展BeanFactory的特性

在本快速教程中,我们将通过实际示例了解这两个 IOC 容器之间的显着差异。

2. 延迟加载与急切加载

 

BeanFactory按需加载 beans,而ApplicationContext在启动时加载所有 beans因此,与ApplicationContext相比, BeanFactory是轻量级的。让我们通过一个例子来理解它。

2.1. 使用BeanFactory进行延迟加载

 

假设我们有一个名为Student的单例 bean类,它有一个方法:

public class Student {
    public static boolean isBeanInstantiated = false;

    public void postConstruct() {
        setBeanInstantiated(true);
    }

    //standard setters and getters
}

 我们将在BeanFactory配置文件ioc-container-difference-example.xml中将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

让我们检查我们手动调用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());
}

在这里,即使我们没有调用getBean()方法,也会创建Student对象。

ApplicationContext被认为是一个重型 IOC 容器,因为它的预加载策略会在启动时加载所有 bean。相比之下,BeanFactory是轻量级的,在内存受限的系统中可以派上用场。尽管如此,我们将在下一节中看到为什么ApplicationContext对于大多数用例来说是首选

三、企业应用特点

 

ApplicationContext以更面向框架的方式增强了BeanFactory,并提供了几个适合企业应用程序的特性。

例如,它提供消息传递(国际化或国际化)功能、事件发布功能、基于注解的依赖注入以及与 Spring AOP 功能的轻松集成

广告

除此之外,ApplicationContext支持几乎所有类型的 bean 作用域,但BeanFactory只支持两种作用域——SingletonPrototype因此,在构建复杂的企业应用程序时最好使用ApplicationContext

4、 BeanFactoryPostProcessorBeanPostProcessor的自动注册

 

ApplicationContext在启动自动注册BeanFactoryPostProcessorBeanPostProcessor另一方面,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()方法注册CustomBeanFactoryPostProcessoraddBeanPostProcessor()方法注册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.结论

 

在本文中,我们通过实际示例了解了ApplicationContextBeanFactory之间的主要区别。

ApplicationContext具有高级功能,包括一些面向企业应用程序的功能,而 BeanFactory具有基本功能。因此,一般推荐使用ApplicationContext 只有在内存消耗很关键的情况下才应该使用BeanFactory

与往常一样,本文的代码可在 GitHub 上获得。