Spring 应用上下文 ApplicationContext
前面一系列文章都是围绕 BeanFactory 进行分析的,BeanFactory 是 Spring 底层 IoC 容器的实现,完成了 IoC 容器的基本功能。在实际的应用场景中,BeanFactory 容器有点简单,它并不适用于生产环境,我们通常会选择 ApplicationContext。ApplicationContext 就是大名鼎鼎的 Spring 应用上下文,它不仅继承了 BeanFactory 体系,还提供更加高级的功能,更加适用于我们的正式应用环境。如以下几个功能:
- 继承 MessageSource,提供国际化的标准访问策略
- 继承 ApplicationEventPublisher ,提供强大的事件机制
- 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源
- 对 Web 应用的支持
ApplicationContext 体系结构
先来看看 ApplicationContext 接口的继承关系
可以看到 ApplicationContext 除了继承 BeanFactory 接口以外,还继承了 MessageSource、ApplicationEventPublisher、ResourceLoader 等接口
简单描述几个接口:
- org.springframework.core.io.ResourceLoader,资源加载接口,用于访问不同的资源
- org.springframework.context.ApplicationEventPublisher,事件发布器接口,支持发布事件
- org.springframework.context.MessageSource,消息资源接口,提供国际化的标准访问策略
- org.springframework.core.env.EnvironmentCapable,环境暴露接口,Spring 应用上下文支持多环境的配置
- org.springframework.context.ApplicationContext,Spring 应用上下文,仅可读
- org.springframework.context.ConfigurableApplicationContext,Spring 应用上下文,支持配置相关属性
接下来我们来看看它们的实现类的继承关系(部分)
简单描述上面几个关键的类:
- org.springframework.context.support.AbstractApplicationContext,Spring 应用上下文的抽象类,实现了大部分功能,提供骨架方法交由子类去实现
- org.springframework.web.context.ConfigurableWebApplicationContext,可配置的 Spring 应用上下文接口,支持 Web 应用
- org.springframework.context.support.AbstractRefreshableConfigApplicationContext,支持设置 XML 文件
- org.springframework.web.context.support.AbstractRefreshableWebApplicationContext,支持 Web 应用
- org.springframework.web.context.support.AnnotationConfigWebApplicationContext,支持 Web 应用,可以设置 XML 文件,并可以扫描注解下面的 Bean
- org.springframework.context.annotation.AnnotationConfigApplicationContext,支持扫描注解下面的 Bean
- org.springframework.web.context.support.ClassPathXmlApplicationContext,支持设置 XML 文件,也可以从 classpath 下面扫描相关资源
ApplicationContext 的子类比较多,主要根据支持 Web、支持注解、支持 XML 文件三个功能进行区分,我们大致了解每个实现类的作用即可。其中基本的实现都是在 AbstractApplicationContext 这个抽象类中完成的,在它的 refresh() 方法体现了 Spring 应用上下文的生命周期。AbstractApplicationContext#refresh() 这个方法可以说是 Spring 应用上下文的准备阶段,在使用 Spring 时该方法会被调用,本文就围绕它进行展述。
可以先看到我的另一篇文章《精尽Spring MVC源码分析 - WebApplicationContext 容器的初始化》,在 Spring MVC 启动过程中,创建 Spring 应用上下文后会调用其 refresh() 方法进行刷新,让 Spring 应用上下文准备就绪。
AbstractApplicationContext
org.springframework.context.support.AbstractApplicationContext,Spring 应用上下文的抽象类,实现了大部分功能,提供骨架方法交由子类去实现
先来看看它的相关属性
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
ContextClosedEvent.class.getName();
}
/** Unique id for this context, if any. */
private String id = ObjectUtils.identityToString(this);
/** Display name. */
private String displayName = ObjectUtils.identityToString(this);
/** 父应用上下文 */
@Nullable
private ApplicationContext parent;
/** 当前应用上下文的环境 */
@Nullable
private ConfigurableEnvironment environment;
/** BeanFactory 的处理器 */
private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();
/** 启动时间 */
private long startupDate;
/** 是否处于激活状态 */
private final AtomicBoolean active = new AtomicBoolean();
/** 是否处于关闭状态 */
private final AtomicBoolean closed = new AtomicBoolean();
/** 启动和销毁时的锁对象 */
private final Object startupShutdownMonitor = new Object();
/** 钩子函数,用于 JVM 关闭时的回调 */
@Nullable
private Thread shutdownHook;
/** ResourcePatternResolver used by this context. */
private ResourcePatternResolver resourcePatternResolver;
/** LifecycleProcessor for managing the lifecycle of beans within this context. */
@Nullable
private LifecycleProcessor lifecycleProcessor;
/** MessageSource we delegate our implementation of this interface to. */
@Nullable
private MessageSource messageSource;
/** 事件广播器 */
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
/** 事件监听器 */
private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
/** 早期(Spring 应用上下文还未就绪)注册的时间监听器 */
@Nullable
private Set<ApplicationListener<?>> earlyApplicationListeners;
/** 早期(Spring 应用上下文还未就绪)发布的事件 */
@Nullable
private Set<ApplicationEvent> earlyApplicationEvents;
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
}
属性不多,上面都有注释
publishEvent 方法
publishEvent(ApplicationEvent event) 方法,发布事件,因为它继承了 ApplicationEventPublisher 事件发布器,如下:
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
// 如果不是 ApplicationEvent 类型的事件,则封装成 PayloadApplicationEvent
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 广播该事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
// 父容器也要发布事件
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
过程如下:
- 如果不是 ApplicationEvent 类型的事件,则封装成 PayloadApplicationEvent
- 如果 earlyApplicationEvents 不为 null,则表示当前 Spring 应用上下文正在处于刷新阶段,还没有准备就绪,则先将这个早期事件添加至 earlyApplicationEvents;否则,Spring 应用上下文已经准备就绪了,此时就对该事件进行广播
- 如果存在父应用上下文,也需要进行广播
上面的第 2 步中的 earlyApplicationEvents 如果不为 null ,为什么 Spring 应用上下文还没有准备就绪呢?答案会在后面体现
addBeanFactoryPostProcessor 方法
addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) 方法,添加 BeanFactoryPostProcessor 处理器,如下:
@Override
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
this.beanFactoryPostProcessors.add(postProcessor);
}
直接往 beanFactoryPostProcessors 添加,BeanFactoryPostProcessor 处理器用于在 Spring 应用上下文刷新阶段对创建好的 BeanFactory 进行后缀处理
addApplicationListener 方法
addApplicationListener(ApplicationListener<?> listener) 方法,添加事件监听器,如下:
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
Assert.notNull(listener, "ApplicationListener must not be null");
if (this.applicationEventMulticaster != null) {
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}
如果事件广播器不为空则将该监听器添加进去,然后再添加到本地的 applicationListeners 中
【核心】refresh 方法
refresh() 方法,Spring 应用上下文的刷新,让 Spring 应用上下文处于准备就绪状态,如下:
/**
* 刷新上下文,在哪会被调用?
* 在 **Spring MVC** 中,{@link org.springframework.web.context.ContextLoader#initWebApplicationContext} 方法初始化上下文时,会调用该方法
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
// <1> 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
synchronized (this.startupShutdownMonitor) {
// <2> 刷新上下文环境的准备工作,记录下容器的启动时间、标记'已启动'状态、对上下文环境属性进行校验
prepareRefresh();
// <3> 创建并初始化一个 BeanFactory 对象 `beanFactory`,会加载出对应的 BeanDefinition 元信息们
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// <4> 为 `beanFactory` 进行一些准备工作,例如添加几个 BeanPostProcessor,手动注册几个特殊的 Bean
prepareBeanFactory(beanFactory);
try {
// <5> 对 `beanFactory` 在进行一些后期的加工,交由子类进行扩展
postProcessBeanFactory(beanFactory);
// <6> 执行 BeanFactoryPostProcessor 处理器,包含 BeanDefinitionRegistryPostProcessor 处理器
invokeBeanFactoryPostProcessors(beanFactory);
// <7> 对 BeanPostProcessor 处理器进行初始化,并添加至 BeanFactory 中
registerBeanPostProcessors(beanFactory);
// <8> 设置上下文的 MessageSource 对象
initMessageSource();
// <9> 设置上下文的 ApplicationEventMulticaster 对象,上下文事件广播器
initApplicationEventMulticaster();
// <10> 刷新上下文时再进行一些初始化工作,交由子类进行扩展
onRefresh();
// <11> 将所有 ApplicationListener 监听器添加至 `applicationEventMulticaster` 事件广播器,如果已有事件则进行广播
registerListeners();
// <12> 设置 ConversionService 类型转换器,**初始化**所有还未初始化的 Bean(不是抽象、单例模式、不是懒加载方式)
finishBeanFactoryInitialization(beanFactory);
// <13> 刷新上下文的最后一步工作,会发布 ContextRefreshedEvent 上下文完成刷新事件
finishRefresh();
}
// <14> 如果上面过程出现 BeansException 异常
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// <14.1> “销毁” 已注册的单例 Bean
destroyBeans();
// <14.2> 设置上下文的 `active` 状态为 `false`
cancelRefresh(ex);
// <14.3> 抛出异常
throw ex;
}
// <15> `finally` 代码块
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
// 清除相关缓存,例如通过反射机制缓存的 Method 和 Field 对象,缓存的注解元数据,缓存的泛型类型对象,缓存的类加载器
resetCommonCaches();
}
}
}
整个过程比较长,每一个步骤都调用一个方法,过程如下:
- 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
- 应用上下文启动准备阶段,调用 prepareRefresh() 方法,说明:刷新上下文环境的准备工作,记录下容器的启动时间、标记'已启动'状态、对上下文环境属性进行校验
- BeanFactory 创建阶段,调用 obtainFreshBeanFactory() 方法,说明:创建并初始化一个 BeanFactory 对象 beanFactory,会加载出对应的 BeanDefinition 元信息们
- BeanFactory 准备阶段,调用 prepareBeanFactory() 方法,说明:为 beanFactory 进行一些准备工作,例如添加几个 BeanPostProcessor,手动注册几个特殊的 Bean
- BeanFactory 后置处理阶段,调用 postProcessBeanFactory(ConfigurableListableBeanFactory) 方法,说明:对 beanFactory 在进行一些后期的加工,交由子类进行扩展
- BeanFactory 后置处理阶段,调用 invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) 方法,说明:执行 BeanFactoryPostProcessor 处理器,包含 BeanDefinitionRegistryPostProcessor 处理器
- BeanFactory 注册 BeanPostProcessor 阶段,调用 registerBeanPostProcessors(ConfigurableListableBeanFactory) 方法,说明:对 BeanPostProcessor 处理器进行初始化,并添加至 BeanFactory 中
- 初始化内建 Bean:MessageSource,调用 initMessageSource() 方法,说明:设置上下文的 MessageSource 对象
- 初始化内建 Bean:Spring 事件广播器,调用 initApplicationEventMulticaster() 方