简述
准备刷新ApplicationContext,初始化一些基础设施。
这是refresh()方法的第一步,主要做一些准备工作:
- 设置启动时间和激活状态
- 初始化属性源(property sources)
- 验证必需的属性
- 准备早期事件集合
核心流程
- 设置启动时间戳和活跃标志
- 初始化属性源(由子类实现)
- 验证所有必需的属性是否已配置
- 创建早期ApplicationEvent集合
源码分析
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 第一步:准备刷新
prepareRefresh();
// ... 后续步骤
}
}
protected void prepareRefresh() {
// 1. 设置启动时间和活跃标志
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 2. 初始化属性源(模板方法,由子类实现)
// 例如:ServletContext参数、JNDI属性等
initPropertySources();
// 3. 验证所有标记为required的属性是否可解析
// 如果有必需属性缺失,会抛出异常
getEnvironment().validateRequiredProperties();
// 4. 存储预刷新阶段的ApplicationListener
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// 重置本地应用监听器为预刷新状态
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// 5. 创建早期事件集合,用于存储在多播器创建之前发布的事件
this.earlyApplicationEvents = new LinkedHashSet<>();
}
/**
* 模板方法,由子类实现以初始化额外的属性源
*/
protected void initPropertySources() {
// 默认空实现,供子类重写
}
}
initPropertySources的实现
ServletWebServerApplicationContext的实现
Web应用需要将ServletContext相关的属性添加到Environment中:
public class GenericWebApplicationContext extends GenericApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
@Override
protected void initPropertySources() {
ConfigurableEnvironment env = getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(
this.servletContext, null);
}
}
}
WebApplicationContextUtils会将以下属性源添加到Environment:
- ServletContext初始化参数:web.xml中的context-param
- ServletConfig初始化参数:servlet配置参数
Environment和PropertySource
Spring的Environment提供了统一的属性访问接口,底层由多个PropertySource组成:
Environment
└── MutablePropertySources
├── systemProperties (System.getProperties())
├── systemEnvironment (System.getenv())
├── servletContextInitParams (web.xml的context-param)
├── servletConfigInitParams
├── jndiProperties
└── applicationConfig (application.properties/yml)
属性查找顺序:从上到下,找到即返回。
验证必需属性
可以通过编程方式设置必需的属性:
@Component
public class ApplicationContextInitializer {
@Autowired
private ConfigurableEnvironment environment;
public void init() {
// 标记某些属性为必需
environment.setRequiredProperties("database.url", "database.username");
// 在prepareRefresh时会验证这些属性
// 如果缺失会抛出MissingRequiredPropertiesException
}
}
或者在SpringApplication启动前设置:
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.addInitializers(context -> {
context.getEnvironment().setRequiredProperties("app.version");
});
app.run(args);
}
}
早期事件处理
在ApplicationEventMulticaster创建之前(在initApplicationEventMulticaster步骤),如果有事件需要发布,会先存储在earlyApplicationEvents集合中。
在ApplicationEventMulticaster创建之后,这些早期事件会被重新发布。
@Override
public void publishEvent(ApplicationEvent event) {
// 如果多播器还未创建,暂存事件
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(event);
}
else {
// 多播器已创建,直接发布
getApplicationEventMulticaster().multicastEvent(event);
}
}
状态标志说明
| 标志 | 类型 | 说明 |
|---|---|---|
| startupDate | long | 容器启动时间戳 |
| active | AtomicBoolean | 容器是否处于活跃状态 |
| closed | AtomicBoolean | 容器是否已关闭 |
这些标志用于:
- 判断容器状态
- 防止重复刷新
- 控制并发访问
参考调用栈
prepareRefresh:587, AbstractApplicationContext (org.springframework.context.support)
refresh:519, AbstractApplicationContext (org.springframework.context.support)
refresh:143, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:755, SpringApplication (org.springframework.boot)
refreshContext:402, SpringApplication (org.springframework.boot)
run:312, SpringApplication (org.springframework.boot)