zoukankan      html  css  js  c++  java
  • 为什么大多数IOC容器使用ApplicationContext,而不用BeanFactory

    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。
    如果你觉得文章还不错,记得关注公众号: 锅外的大佬
    刘一手的博客

  • 相关阅读:
    JavaSript模块化 && AMD CMD 详解.....
    js实现touch移动触屏滑动事件
    页面布局之BFC 微微有点坑
    前端代码优化
    HTTP消息头详解
    SASS
    移动互联,手机页面设计
    投身移动开发必须知道的20件事
    浅析HTML5在移动应用开发中的使用
    js数组的操作
  • 原文地址:https://www.cnblogs.com/liululee/p/13967437.html
Copyright © 2011-2022 走看看