spring源码系列01-BeanFactory和ApplicationContext的关系

平时经常遇到的一个问题就是问BeanFactoryApplicationContext这两种容器的区别,这篇文章从另一个角度,这两种容器之间的关系来谈起,对这两种容器进行一个介绍和对比。

先说结论: ApplicationContext类型容器的内部维护了一个BeanFactory类型的容器,bean都是存在这个内部BeanFactory里的

当然,这两个都是接口,真正使用的容器是它们的实现类

一、从类图开始

首先来看一下类图

BeanFactory和ApplicationContext的类图
从上边的类图上我们可以看出这几个要点:

(1) ApplicationContextBeanFactory的子接口,所以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开发实战,欢迎大家关注