Spring 源码解析:prepareRefresh() 方法深度剖析与面试指南
引言
在 Spring 框架中,AbstractApplicationContext#refresh() 是容器初始化的核心入口方法,而 prepareRefresh() 是其第一个关键步骤。该方法负责容器的启动前准备工作,包括环境初始化、状态校验、资源预加载等。本文将通过源码逐行分析 prepareRefresh(),并总结高频面试考点。
一、prepareRefresh() 方法的作用
prepareRefresh() 是 Spring 容器启动流程的初始化前哨,主要完成以下任务:
- 记录容器启动时间戳。
- 设置容器的活跃/关闭状态标识。
- 初始化环境配置(Environment)。
- 校验环境中的必要参数。
- 初始化早期事件(Early Application Events)存储队列。
二、源码逐行解析
1. 方法签名与入口
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis(); // 记录启动时间
this.closed.set(false); // 标记容器未关闭
this.active.set(true); // 标记容器已激活
// 初始化日志(根据环境配置)
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// 初始化占位符属性源(如PropertySources)
initPropertySources();
// 验证环境中的必要属性
getEnvironment().validateRequiredProperties();
// 初始化早期事件监听器集合
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
this.earlyApplicationEvents = new LinkedHashSet<>();
}
2. 关键操作详解
-
startupDate记录
标记容器启动时间,用于后续性能监控或生命周期追踪。 -
状态标识 (
closed/active)
通过原子布尔变量保证多线程环境下的状态安全,避免容器重复启动或关闭。 -
initPropertySources()
核心作用:初始化或扩展环境属性源(如系统变量、JNDI、配置文件等)。
扩展点:子类(如WebApplicationContext)可重写此方法,添加自定义属性源。
例如,在 Web 环境中会添加ServletContext和ServletConfig属性源。 -
validateRequiredProperties()
校验环境中必须存在的属性。若缺失,直接抛出MissingRequiredPropertiesException。
例如,通过@Value("${required.prop}")注入的属性必须在此处声明。 -
早期事件队列初始化
在事件广播器(ApplicationEventMulticaster)初始化前,临时存储早期事件(如ContextRefreshedEvent),待广播器就绪后发布。
三、高频面试考点与答案
1. prepareRefresh() 在容器启动流程中的角色是什么?
- 答案:它是容器启动的第一个步骤,负责初始化环境、校验配置、准备事件队列,为后续 Bean 加载和依赖注入奠定基础。
2. initPropertySources() 的设计意义是什么?
- 答案:这是一个典型的模板方法模式应用。父类定义算法骨架,子类(如
AnnotationConfigWebApplicationContext)通过重写该方法扩展属性源,实现环境定制化。
3. 如何强制要求某个属性必须配置?
- 答案:在环境(
Environment)中调用setRequiredProperties("key"),若属性缺失,validateRequiredProperties()将抛出异常。
4. 早期事件队列的作用是什么?
- 答案:在事件广播器初始化前,临时存储监听器注册和事件发布,避免事件丢失。广播器就绪后,立即处理这些事件。
5. 如何扩展 Spring 的环境属性源?
- 答案:通过重写
initPropertySources()方法,调用getEnvironment().getPropertySources().addLast()添加自定义属性源(如数据库配置)。
四、总结与扩展
- 设计模式:
prepareRefresh()体现了模板方法模式(refresh()方法流程固定,子类可扩展具体步骤)。 - 性能优化:通过
earlyApplicationEvents避免事件阻塞,提升容器启动效率。 - 安全校验:
validateRequiredProperties()是 Spring Boot 的@ConditionalOnProperty等条件注解的底层依赖。
附:典型面试题
Q: 如果在 prepareRefresh() 阶段发现必要属性缺失,Spring 会如何处理?
A: 抛出 MissingRequiredPropertiesException,阻止容器继续启动。
Q: 如何在不修改源码的情况下扩展环境属性源?
A: 实现 ApplicationContextInitializer 接口,在 initialize() 方法中操作 ConfigurableEnvironment 的 PropertySources。
通过深入理解 prepareRefresh(),开发者不仅能掌握 Spring 容器的启动机制,还能灵活应对环境配置、属性校验等实际需求。在面试中,结合源码和设计模式回答此类问题,将显著提升技术深度评价。