ApplicationContext容器
1.概述
ApplicationContext接口代表了一个Spring容器,它主要负责实例化、配置和组装bean。ApplicationContext接口间接继承了BeanFactory接口,相较于BeanFactory一些基本的容器功能,ApplicationContext接口是在BeanFactory接口基础上进行了扩展,增加了国际化、事件广播、获取资源等一些新的功能。
2.ApplicationContext系列类图
从以上类图中可以看出,ApplicationContext接口的派生体系,是一个非常庞大的家族。
- FileSystemXmlApplicationContext:默认从文件系统中加载bean定义信息的ApplicationContext实现。
- ClassPathXmlApplicationContext:默认从ClassPath中加载bean定义信息的ApplicationContext实现。
- XmlWebApplicationContext:专门用于Web应用程序的ApplicationContext实现。SpringMVC 中默认使用的容器。
- AnnotationConfigApplicationContext:是一个基于注解配置类的ApplicationContext实现。SpringBoot 中默认使用的容器。
- AnnotationConfigServletWebServerApplicationContext:是SpringBoot一个基于注解配置类的servlet web应用程序的ApplicationContext实现。容器中会一个内置的servlet 服务器。
- AnnotationConfigReactiveWebServerApplicationContext:是SpringBoot一个基于基于注解配置类的reactive web应用程序的ApplicationContext实现。容器中会一个内置的reactive 服务器。
3.refresh()方法
3.1概述
refresh方法是Spring容器中一个非常核心的方法。经过refresh方法后,一个完整的Ioc容器已经创建完成。refresh方法是在ConfigurableApplicationContext接口定义的,而给出具体这个方法实现的是在AbstractApplicationContext的类中。
从refresh方法的源码可以发现,refresh方法中调用了12个子方法。这12个子方法其实就是Spring创建Ioc容器的12个步骤。
refresh方法其实是使用了模版方法模式。模板方法模式定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以在不改变一个算法结构的情况下,重定义该算法的某些特定步骤。
refresh方法中调用的12个步骤方法,为Ioc容器的创建定义了一个总体框架。在各种具体的ApplicationContext的子类中,会根据自身具体的特性,再对这12个步骤方法进行重写,但是创建容器的总体步骤是不变的。
3.2源码
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备刷新上下文环境:设置其启动日期和活动标志,初始化上下文环境中的占位符属性源
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//实例化DefaultListableBeanFactory实例并返回,对BeanFactory进行定制,加载BeanDefinition的信息
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//配置beanFactory容器的特性
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//在容器初始化后,预留在子类中的上下文中,可以对容器进行修改
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 执行注册在容器中的各种BeanFactory的处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册Bean的各种处理器。处理器会在创建bean时调用。
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化上下文的Message源。如:i18n国际化处理
initMessageSource();
// Initialize event multicaster for this context.
//为上下文初始化应用程序广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//在特定的上下文中,留给子类来的初始化其他的特殊bean
onRefresh();
// Check for listener beans and register them.
// 查看ApplicationListener类型的bean,并注册他们。
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 对上下文环境中剩余的单例bean完成初始化(非惰性的bean)
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 调用LifecycleProcessor生命周期处理器的onRefresh方法并发布ContextRefreshedEvent通知,
// 来完成上下文的刷新过程。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
//销毁上下文容器中所有缓存的单例bean,已避免占用资源。
destroyBeans();
// Reset 'active' flag.
//取消此上下文的刷新尝试,并重置active标志。
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
3.2.1prepareRefresh
prepareRefresh方法主要是对上下文的刷新做了一些准备工作,该方法中主要做了以下几件事情。
- 激活开关
prepareRefresh方法的激活开关,主要是程序中记录了当前的时间戳,打开了当前上下文是否处于活动状态的标志(设置为true) ,关闭当前上下文是否处于关闭状态的标志(设置为false)。
- 初始化占位符
initPropertySources方法是用于初始化上下文环境中的占位符属性。但是在AbstractApplicationContext类中,initPropertySources方法只是一个空方法,没有任何实现代码。该方法是专门预留给子类扩展实现用的。
我们可以自己实现一个继承至AbstractApplicationContext的子类,并在子类的initPropertySources方法中,根据自己的需求来设置需要验证的占位符。
- 对占位符属性进行验证
Spring对占位符属性进行验证时,先获取ConfigurableEnvironment类型的实例,然后再调用这个实例的validateRequiredProperties方法来进行验证。
在validateRequiredProperties方法中,会判断占位符的属性值是否为空,若值为空,就会抛出异常。默认情况下,一般是对系统中的环境变量和JVM的环境变量来进行判断。
3.2.2obtainFreshBeanFactory
obtainFreshBeanFactory方法从方法名上,顾名思义就是获取BeanFactory容器。Spring经过这个函数之后,ApplicationContext就已经拥有了BeanFactory 的全部功能。obtainFreshBeanFactory方法很简单,它主要调用了两个方法,这两个方法分别是refreshBeanFactory和getBeanFactory方法。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//创建BeanFactory容器实例
refreshBeanFactory();
//返回创建的BeanFactory容器实例
return getBeanFactory();
}
refreshBeanFactory方法
refreshBeanFactory方法的作用主要是创建BeanFactory容器的实例。refreshBeanFactory方法在AbstractApplicationContext类中只是一个虚方法,没有给出具体的实现。这个方法的具体实现是在子类AbstractRefreshableApplicationContext类中给出的。具体源码如下所示:
protected final void refreshBeanFactory() throws BeansException {
//判断是否已经创建了BeanFactory容器
//如果已经创建,则销毁容器中的bean,并关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//使用new方式创建一个DefaultListableBeanFactory的容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
//给容器设置一个序列化的id
beanFactory.setSerializationId(getId());
//自定义DefaultListableBeanFactory容器的相关属性
customizeBeanFactory(beanFactory);
//给容器加载BeanDefinition
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
通过以上源码可以发现refreshBeanFactory方法主要做了以下几件事情:
- 创建DefaultListableBeanFactory容器
对于DefaultListableBeanFactory容器的创建, Spring中直接是new的方式创建了一个DefaultListableBeanFactory的实例,非常简单。DefaultListableBeanFactory是Bean工厂的一个默认实现,它提供了容器的基本功能。
- 给容器指定一个序列化的id
- 自定义容器的相关属性
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
customizeBeanFactory方法用于自定义容器的相关属性。方法中判断了allowBeanDefinitionOverriding和allowCircularReferences这两个属性的值是否为空,不为空的话,就对容器进行属性值设置。
allowBeanDefinitionOverriding属性表示是否允许覆盖同名的BeanDefinition,默认值是true。
allowCircularReferences属性表示是否允许bean之间能否进行循环依赖,默认值也是true。
- 给容器加载BeanDefinition
BeanDefinition定义了描述Bean的元数据信息。BeanDefinition的加载,其实就是把Spring外部对Bean的定义信息转化成IoC容器中内部数据结构的过程。
IoC容器对Bean的管理和依赖注入功能的实现,其实都是通过对其持有的BeanDefinition进行各种相关操作来完成的。
Spring外部对BeanDefinition的定义是多种形式的,BeanDefinition的定义有xml文件,properties文件和注解等形式。对于这三种形式的BeanDefinition,Spring分别提供了XmlBeanDefinitionReader,PropertiesBeanDefinitionReader,AnnotatedBeanDefinitionReader三个类来加载相应的BeanDefinition信息。
getBeanFactory方法
getBeanFactory方法的作用主要是返回已经创建的BeanFactory容器实例,比较简单,无需展开。
3.2.3prepareBeanFactory
prepareBeanFactory方法主要是对beanFactory容器的特性进行一些配置的准备工作。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
//设置容器的ClassLoader
beanFactory.setBeanClassLoader(getClassLoader());
//设置容器的SpEL语言解析器,增加对SpEL语言的支持。
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
//添加容器的属性编辑器
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
//为容器注册ApplicationContextAwareProcessor的后置处理器
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//设置容器忽略自动装配的接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
//对容器注册自动装配所依赖特殊类型
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerRe