spring源码系列01-BeanFactory和ApplicationContext的关系
平时经常遇到的一个问题就是问BeanFactory
和ApplicationContext
这两种容器的区别,这篇文章从另一个角度,这两种容器之间的关系来谈起,对这两种容器进行一个介绍和对比。
先说结论: ApplicationContext类型容器的内部维护了一个BeanFactory类型的容器,bean都是存在这个内部BeanFactory里的
当然,这两个都是接口,真正使用的容器是它们的实现类
一、从类图开始
首先来看一下类图
从上边的类图上我们可以看出这几个要点:
(1) ApplicationContext
是BeanFactory
的子接口,所以BeanFactory的功能ApplicationContext都有
(2) ApplicationContext
又实现了几个接口,这每一个接口都代表一种功能,而这些功能是BeanFactory类型容器没有的。比如ApplicationEventPublisher
这个接口,给spring容器提供了以类似发布订阅的形式进行事件处理的功能;EnvironmentCapable
接口给spring容器提供了获取环境变量的能力;MessageSource
接口给spring容器提供了国际化处理的能力。
二、一段spring入门代码
在这里我们先使用一下ApplicationContext容器,来引出这两种容器之间的关系。我们演示下如何以xml的形式来使用spring,使用的容器是ClassPathXmlApplicationContext
这个ApplicationContext的实现类。
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.lyy.vo.Person"></bean>
</beans>
java代码
public class Test01 {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = app.getBean(Person.class);
System.out.println(person);
}
}
上边的一段代码,是我们学习spring时常写的一段代码,其中app是创建出来的容器实例,通过它可以获取我们需要的Bean。那么我们提出一个问题,ApplicationContext类型的容器中具体的Bean是在哪里存储的?
要回答这个问题,我们可以跟一下app.getBean(Person.class)方法,它是从哪里取的值,那Bean就是存在哪里的。
三、ApplicationContext容器中如何存储Bean的探索
先给出ClassPathXmlApplicationContext
的类图,方便后边的讲解。
首先,点进去app.getBean方法,来到AbstractApplicationContext.getBean()
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
从这个方法最后一句的getBeanFactory()
,我们可以猜测,ClassPathXmlApplicationContext容器内部有一个
BeanFactory
容器,Bean都是存在这个里边的。继续跟进getBeanFactory()方法,发现这个方法是定义在
AbstractApplicationContext
中的一个抽象方法,针对xml方式使用,具体的实现是在
AbstractRefreshableApplicationContext
中,这里要结合上边的类图看,会更好理解。
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
DefaultListableBeanFactory beanFactory = this.beanFactory;
if (beanFactory == null) {
throw new IllegalStateException
("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return beanFactory;
}
从这段代码看,返回的这个BeanFactory确实是AbstractRefreshableApplicationContext
中的一个属性,这就验证了我们上边的猜想,ApplicationContext类型容器的内部维护了一个BeanFactory类型的容器
针对ClassPathXmlApplicationContext,这个BeanFactory是维护在它的父类当中的
再去看下这个属性的类型
/** Bean factory for this context. */
@Nullable
private volatile DefaultListableBeanFactory beanFactory;
这是AbstractRefreshableApplicationContext
类中定义属性的地方,这里的注释也侧面验证了上边的猜想。
DefaultListableBeanFactory
这个BeanFactory的实现类,才是真正的存储springBean的。
四、总结
通过上边的分析,我们得到了一个重要的结论:ApplicationContext类型容器的内部维护了一个BeanFactory类型的容器,bean都是存在这个内部BeanFactory里的。这就是这两种容器之间的关系。
至于区别,除了上边给出的 ApplicationContext多了事件处理,环境变量,国际化处理外,还有很重要的一点,
BeanFactory类型的容器中的Bean是懒加载的,获取的时候才会创建,而ApplicationContext中的bean,容器启动完成后就已经创建好了,这个在后边的文章中会具体分析。
本文已同步发表在我的个人公众号java开发实战,欢迎大家关注