BeanFactory和ApplicationContext的区别

267

我对Spring Framework还比较新,一直在尝试使用它并编写了一些示例应用程序来评估Spring MVC是否适合我们公司即将启动的项目。到目前为止,我真的很喜欢Spring MVC中看到的东西,它似乎非常易于使用,并鼓励您编写非常适合单元测试的类。

就像一个练习,我正在为我的一个示例/测试项目编写主方法。我不清楚BeanFactoryApplicationContext之间的确切区别-哪个适用于哪种情况?

我知道ApplicationContext扩展了BeanFactory,但是如果我只是编写一个简单的主方法,我需要ApplicationContext提供的额外功能吗?ApplicationContext提供了什么样的额外功能呢?

除了回答“我应该在main()方法中使用哪个”之外,还有哪些标准或指导方针可以在这种情况下选择哪种实现?我的main()方法是否应编写以依赖于XML格式的bean/application配置-这是一个安全的假设,还是我将用户锁定在某些特定的内容中?

并且,在Web环境中是否会更改此答案-如果我的任何类需要意识到Spring,它们是否更可能需要ApplicationContext

感谢您的帮助。我知道很多这些问题可能已在参考手册中回答,但是我很难找到这两个接口的清晰细分以及每个接口的优缺点,而不必仔细阅读手册。

22个回答

229
这方面的Spring文档非常好:3.8.1. BeanFactory or ApplicationContext?。 他们有一个比较表,这里是一部分:

Bean工厂

  • Bean实例化/连接

应用上下文

  • Bean实例化/连接
  • 自动BeanPostProcessor注册
  • 自动BeanFactoryPostProcessor注册
  • 便捷的MessageSource访问(用于i18n)
  • ApplicationEvent发布

因此,如果您需要应用程序上下文一侧呈现的任何要点,则应使用ApplicationContext。


3
BeanFactory 轻量级,但如果你要真正使用 Spring 的话,最好选择 ApplicationContext:如果不使用其高级功能,则几乎没有额外的开销,但一旦需要使用这些功能时,它们仍然可用。 - MetroidFan2002
3
“automatic BeanPostProcessor registration” 的意思是什么?这是否意味着类不必实现该接口? - Abidi
2
ApplicationContext 支持针对 BeanFactory 的 AOP。 - ininprsr
1
使用 BeanFactory 我们可以动态地传递构造函数参数,但是使用 ApplicationContext 就不能这样做。 - Half Blood Prince
3
这里有来自Spring文档的重要提示:“Spring 2.0及以上版本大量使用了BeanPostProcessor扩展点(用于实现代理等),如果您仅使用纯BeanFactory,则会缺少相当多的支持,例如事务和AOP将不会生效(至少需要您进行一些额外的步骤)。" - mark.monteiro
显示剩余2条评论

78
Spring提供了两种IOC容器,一种是XMLBeanFactory,另一种是ApplicationContext。
BeanFactory | ApplicationContext ---|--- 注解支持 | 否 | 是 BeanPostProcessor注册 | 手动 | 自动 实现 | XMLBeanFactory | ClassPath/FileSystem/WebXmlApplicationContext 国际化 | 否 | 是 企业服务 | 否 | 是 ApplicationEvent发布 | 否 | 是

enter image description here

  • FileSystemXmlApplicationContext:通过完整路径加载Bean。
  • ClassPathXmlApplicationContext:通过CLASSPATH加载Bean。
  • XMLWebApplicationContextAnnotationConfigWebApplicationContext:通过Web应用程序上下文加载Bean。
  • AnnotationConfigApplicationContext:从基于注释的配置中加载Spring Bean。
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeansConfiguration.class);
  • ApplicationContext 是由在 web.xml 中定义的 ContextLoaderListenerContextLoaderServlet,以及在 struts-config.xml 中定义的 ContextLoaderPlugin 初始化的容器。

注意: 从 Spring 3.1 开始,XmlBeanFactory 已经弃用,推荐使用 DefaultListableBeanFactoryXmlBeanDefinitionReader


2
在图表中,AnnotationConfigApplicationContext应该位于ClassPathXmlApplicationContext下方,而不是AnnotationConfigWebApplicationContext。 - Akhil Jain
感谢形象的解释。这很有帮助! - Vishal A

51
对我而言,选择 BeanFactory 而非 ApplicationContext 的主要区别似乎在于 ApplicationContext 将预先实例化所有的 bean。从 Spring 文档 可知:

Spring 尽可能地晚设置属性和解决依赖关系,即在实际创建 bean 时才这么做。这意味着,当 Spring 容器正确加载后,如果创建该对象或其任何依赖项存在问题,则可以稍后生成异常。例如,由于缺少或无效的属性,bean 抛出异常。这些潜在延迟可见性的某些配置问题是 ApplicationContext 实现默认预先实例化 singleton beans 的原因。为了在实际需要之前创建这些 beans 并付出一些前期时间和内存的成本,您可以在 ApplicationContext 创建时发现配置问题,而不是以后。您仍然可以覆盖此默认行为,使 singleton beans 延迟初始化,而不是被预先实例化。

鉴于此,我最初选择 BeanFactory 用于集成/性能测试,因为我不希望为测试隔离的 beans 加载整个应用程序。然而——请纠正我如果我错了——BeanFactory 不支持 classpath XML 配置。因此,BeanFactoryApplicationContext 各提供了我想要的关键功能,但两者都不是全能的。
据我所知,文档中关于覆盖默认实例化行为的说明发生在配置中,且是针对每个 bean 的,所以我不能只在 XML 文件中设置 "lazy-init" 属性,否则我就必须为测试和部署维护两个版本。
我最终做的是扩展 ClassPathXmlApplicationContext 以懒加载 beans 用于测试,代码如下:
public class LazyLoadingXmlApplicationContext extends ClassPathXmlApplicationContext {

    public LazyLoadingXmlApplicationContext(String[] configLocations) {
        super(configLocations);
    }

    /**
     * Upon loading bean definitions, force beans to be lazy-initialized.
     * @see org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)
     */

    @Override
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        super.loadBeanDefinitions(reader);
        for (String name: reader.getBeanFactory().getBeanDefinitionNames()) {
            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) reader.getBeanFactory().getBeanDefinition(name);
            beanDefinition.setLazyInit(true);
        }
    }

}

2
我认为,如果你的单元测试正在加载完整的Spring上下文,那么它们不是“单元测试”,而应该称作集成测试。 - matt b
1
好的观点。在我的情况下,我实际上需要从上下文中加载bean以进行性能和集成测试,并出于习惯编写了“单元测试”。我已相应地编辑了我的答案。 - Lyle
2
BeanFactory不支持类路径XML配置。我认为它是支持的:https://dev59.com/DW435IYBdhLWcg3wvy-_ - Aritz

33
补充一下Miguel Ping的回答,这里还有一个来自文档的章节(传送门) ,也回答了这个问题:

简短版:除非你有非常好的理由不这样做,否则请使用ApplicationContext。对于那些想要更深入理解此建议背后“为什么”的人,请继续阅读。

(发布此内容是为了给未来可能会阅读这个问题的Spring新手)


21
  1. ApplicationContextBeanFactory 更受欢迎。

  2. 在新的Spring版本中,BeanFactoryApplicationContext 取代。但是为了向后兼容,BeanFactory 仍然存在。

  3. ApplicationContext 扩展了 BeanFactory 并具有以下优点:
    • 支持文本消息的国际化
    • 支持将事件发布给已注册的监听器
    • 访问资源(例如URL和文件)

14

ApplicationContext: 它加载在Spring配置文件中配置的Spring bean,并在容器启动时管理Spring bean的生命周期。它不会等待调用getBean("springbeanref")

BeanFactory 它加载在Spring配置文件中配置的Spring bean,并在调用getBean("springbeanref")时管理Spring bean的生命周期。因此,当我们在Spring bean生命周期开始时调用getBean("springbeanref")


13

我认为最好总是使用ApplicationContext,除非你像其他人所说的那样处于移动环境。ApplicationContext具有更多功能,您一定要使用PostProcessors,例如RequiredAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor等,这些都将帮助简化Spring配置文件,您可以在bean中使用@Required、@PostConstruct、@Resource等注释。

即使您不使用ApplicationContext提供的所有内容,仍然最好使用它,然后稍后如果您决定使用某些资源(如消息或后处理器)或其他模式来添加事务通知等,您已经拥有了一个ApplicationContext并且不需要更改任何代码。

如果您正在编写独立应用程序,请在主方法中使用ClassPathXmlApplicationContext加载ApplicationContext,并获取主要bean并调用其run()(或其他方法)以启动应用程序。如果您正在编写Web应用程序,请在web.xml中使用ContextLoaderListener创建ApplicationContext,这样您可以稍后从ServletContext获取它,而无论您是使用JSP、JSF、JSTL、struts、Tapestry等。

另外,请记住您可以使用多个Spring配置文件,您可以通过在构造函数中列出所有文件(或在ContextLoaderListener的context-param中列出它们),或者您可以只加载一个含有import语句的主配置文件。您可以使用<import resource="otherfile.xml" />将Spring配置文件导入另一个Spring配置文件中,这在您以编程方式创建ApplicationContext并仅加载一个Spring配置文件时非常有用。


9
BeanFactoryApplicationContext的区别如下:
  1. BeanFactory使用延迟初始化,但是ApplicationContext使用急切初始化。在BeanFactory的情况下,当你调用getBeans()方法时才创建bean,但是在ApplicationContext的情况下,在创建ApplicationContext对象时就预先创建bean。
  2. BeanFactory通过语法明确提供资源对象,但是ApplicationContext自己创建和管理资源对象。
  3. BeanFactory不支持国际化,但是ApplicationContext支持国际化。
  4. 在BeanFactory中不支持基于注解的依赖注入,但是在ApplicationContext中支持基于注解的依赖注入。
使用BeanFactory:
BeanFactory beanfactory = new XMLBeanFactory(new FileSystemResource("spring.xml"));
 Triangle triangle =(Triangle)beanFactory.getBean("triangle");

使用ApplicationContext:

ApplicationContext context = new ClassPathXMLApplicationContext("spring.xml")
Triangle triangle =(Triangle)context.getBean("triangle");

6

我们可以用两种方式创建Spring容器对象:

  1. 使用BeanFactory。
  2. 使用ApplicationContext。

这两个都是接口,

我们可以使用实现类为Spring容器创建对象。

关于它们的区别:

BeanFactory:

  1. 不支持基于注解的依赖注入。

  2. 不支持I18N。

  3. 默认支持延迟加载。

  4. 它不允许配置多个配置文件。

例如:BeanFactory context=new XmlBeanFactory(new Resource("applicationContext.xml"));

ApplicationContext:

  1. 支持基于注解的依赖注入-@Autowired、@PreDestroy。

  2. 支持I18N。

  3. 默认支持积极加载。

  4. 它允许配置多个配置文件。

例如:
ApplicationContext context=new ClasspathXmlApplicationContext("applicationContext.xml");


6

通常情况下,ApplicationContext 更受欢迎,除非你需要节省资源,例如在移动应用程序中。

我不确定是否依赖 XML 格式,但我很确定最常见的 ApplicationContext 实现是 XML 类型的,例如 ClassPathXmlApplicationContext、XmlWebApplicationContext 和 FileSystemXmlApplicationContext。这些是我曾经使用过的三个实现。

如果您正在开发 Web 应用程序,则应该使用 XmlWebApplicationContext。

如果您希望您的 bean 知道 Spring,请让它们实现 BeanFactoryAware 和/或 ApplicationContextAware 接口,这样您就可以使用 BeanFactory 或 ApplicationContext,并选择要实现哪个接口。


这是文档中相关的部分由于ApplicationContext包含了BeanFactory的所有功能,因此通常建议除了在一些有限的情况下(例如在Applet中,内存消耗可能很关键,几个额外的千字节可能会有所不同)之外,都应该优先使用它而不是BeanFactory。然而,对于大多数“典型”的企业应用程序和系统,您将希望使用ApplicationContext - M. Atif Riaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接