为什么大多数IOC容器使用ApplicationContext,而不用BeanFactory
1. 引言
Spring框架附带了两个IOC容器– BeanFactory 和 ApplicationContext. BeanFactory是IOC容器的最基本版本,ApplicationContext扩展了BeanFactory的功能。
那么本篇文章中,我们将通过实际例子了解这两个IOC容器之间的显著差异。
2. 延迟加载 vs. 预加载
BeanFactory 按需加载bean,而 ApplicationContext 则在启动时加载所有bean。因此,BeanFactory与ApplicationContext相比是轻量级的。让我们用一个例子来理解它。
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只支持两个作用域——Singleton和Prototype。因此,在构建复杂的企业应用程序时,最好使用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
}
这里,我们重写了 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。
如果你觉得文章还不错,记得关注公众号: 锅外的大佬
刘一手的博客
为什么大多数IOC容器使用ApplicationContext,而不用BeanFactory的更多相关文章
- Spring的IoC容器-Spring ApplicationContext容器
Application Context是spring中较高级的容器.和BeanFactory类似,它可以加载配置文件中定义的bean,将所有的bean集中在一起,当有请求的时候分配bean. 另外,它 ...
- 自定义模拟一个Spring IOC容器
一.模拟一个IOC容器: 介绍:现在,我们准备使用一个java project来模拟一个spring的IOC容器创建对象的方法,也就是不使用spring的jar自动帮助我们创建对象,而是通过自己手动书 ...
- SPRING源码分析:IOC容器
在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求: ...
- spring框架--IOC容器,依赖注入
思考: 1. 对象创建创建能否写死? 2. 对象创建细节 对象数量 action 多个 [维护成员变量] service 一个 [不需要维护公共变量] dao 一个 [不需要维护 ...
- 【转】Spring学习---Spring IoC容器的核心原理
[原文] Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国. IoC和DI的基本概念 IoC(控制反转,英文含义:Inverse of Control)是Spr ...
- Java进阶知识16 Spring创建IOC容器的两种方式
1.直接得到 IOC 容器对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app ...
- 对Spring IoC容器实现的结构分析
本文的目标:从实现的角度来认识SpringIoC容器. 观察的角度:从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringIoC容器. 本文的风格:首先列出SpringIoC的外部接口及内 ...
- Spring 中 IoC 容器简介
IoC 是一种通过描述来生成或者获取对象的技术,可以说 Spring 是一种基于 IoC 容器编程的框架 在一个系统中可以生成各种对象,并且这些对象都需要进行管理.为了描述这些对象关系,我们需要一个容 ...
- SpringIOC的概念理解、构造器注入、setter注入、p命名空间注入、IOC容器介绍与比较
1.IOC概念理解 IOC(Inversion of Control)即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象 ...
随机推荐
- 启动你的Android应用:运行设备模拟器和调试代码(第3部分)
下载all source for Test Proj: Test.zip - 306 KB 文章指出 本文包含了我即将出版的新书<启动Android应用程序>中的第三章. 在我完成这本书之 ...
- 在自己电脑上查看git远程分支列表比实际云端的远程分支要多一些
问题 最近打开一个很久没有打开过的项目,使用git branch -a查看了一下所以分支,其中有些远程分支没有什么用了 于是准备去gitlab上删除它,结果到gitlab上发现没有这些分支,猜测是自己 ...
- Springboot+JPA下实现简易爬虫:豆瓣电视剧数据
Springboot+JPA下实现简易爬虫:豆瓣电视剧数据 前言:今天听到产品那边讨论一些需求,好像其中一点是用户要求我们爬虫,在网页上抓取一些数据然后存到我们公司数据库中,众所周知,爬虫的实现对于p ...
- 在Jenkins容器中安装docker-compose
首先使用Docker容器安装Jenkins 链接参考 安装成功后使用管理员权限进入到Jenkins容器 docker exec -it -u root jenkins bash 下载docker-co ...
- MySQL的事务与锁 转
资料来源: 青山老师的mysql课程 丁奇老师的<MySQL45讲> 一.文章结构和思路 1.整体文章结构 2.大概思路 介绍数据库的特性: 数据库标准所制定的隔离级别和解决对应的一致性问 ...
- Solr6.4.2异常:org.apache.solr.common.SolrException: Error opening new searcher
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明. 原文链接:https://www.cnblogs.com/chenghu/p/13840021.html Solr版本6.4.2 启动S ...
- 你真的了解Python吗?这篇文章可以让你了解90%
人们为什么使用Python? 之所以选择Python的主要因素有以下几个方面: 软件质量:在很大程度上,Python更注重可读性.一致性和软件质量,从而与脚本语言世界中的其他工具区别开发.此外,Pyt ...
- 503. 下一个更大元素 II
503. 下一个更大元素 II 给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素.数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大 ...
- 【Vue.js】简单说下vuejs中v-model自定义使用姿势
vue.js中有个v-model的语法,可以实现双向绑定. 起初刚看到的时候,觉得很神奇.后面随着对vue.js的熟悉.发现这个其实是vue官方给我们实现的一个语法糖. 使用v-model的时候,vu ...
- 一分钟了解"秒杀"系统
关于秒杀,第一反应都是实现起来比较复杂.难点在于:并发读+并发写+设计兜底方案的实现. 比如QQ,虽然数据量很大,但是多数的数据都是细粒度的数据查询,锁冲突比较少:但12306涉及到大量的读写操作,对 ...