关于spring:为什么大多数IOC容器使用ApplicationContext而不用BeanFactory

51次阅读

共计 5376 个字符,预计需要花费 14 分钟才能阅读完成。

1. 引言

Spring 框架附带了两个 IOC 容器– BeanFactoryApplicationContext. BeanFactory是 IOC 容器的最根本版本,ApplicationContext扩大了 BeanFactory 的性能。
那么本篇文章中,咱们将通过理论例子理解这两个 IOC 容器之间的显著差别。

2. 提早加载 vs. 预加载

BeanFactory 按需加载 bean,而 ApplicationContext 则在启动 时加载所有 bean。因而,BeanFactoryApplicationContext 相比是轻量级的。让咱们用一个例子来了解它。

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"/>

当初,让咱们编写一个测试用例来创立一个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 只反对两个作用域——SingletonPrototype。因而,在构建简单的企业应用程序时,最好应用ApplicationContext

4. 主动注册 BeanFactoryPostProcessorBeanPostProcessor

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" 
  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,因为 Spring2.0(及更高版本)大量应用 BeanPostProcessor
还有一点值得注意的是 如果应用的是一般的 BeanFactory,那么事务和 AOP 之类的性能将不会失效(除非你编写额定的代码实现,那就另当别论了)。这样可能会导致代码很凌乱,因为配置看起来貌似没故障。

5. 写在结尾

ApplicationContext 提供了一些高级性能,包含一些面向企业应用程序的性能,而 BeanFactory 只提供了基本功能。因而,个别倡议应用 ApplicationContext,只有在内存耗费十分要害的状况下,咱们才应该思考去应用 BeanFactory。
如果你感觉文章还不错,记得关注公众号:锅外的大佬
刘一手的博客

正文完
 0