BootstrapContext 与 BootstrapRegistry 实现
概述
BootstrapContext 是 Spring Boot 3.x 引入的启动阶段上下文,在 SpringApplication.run() 的最早期创建,远在 Environment 和 ApplicationContext 准备之前。它提供了一个轻量级的注册表,供启动早期的初始化器交换数据。
本文深入拆解 DefaultBootstrapContext 的内部结构、Scope 控制、异常语义和生命周期清理等实现细节。
本文基于 Spring Boot 3.x 源码分析。
1. 整体架构与接口层次
BootstrapRegistry ← 核心注册接口(注册/获取/关闭)
│
├── BootstrapContext ← 查询接口(只读,聚焦 get)
│ └── ConfigurableBootstrapContext ← 可配置子接口(增加 register/close)
│
└── DefaultBootstrapContext ← 唯一实现类1.1 接口定义
java
// BootstrapRegistry.java —— 注册与获取的接口
public interface BootstrapRegistry {
/** 注册一个启动阶段实例 */
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);
/** 注册带 scope 控制的实例 */
<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier,
Scope scope);
/** 检查是否包含某类型 */
boolean contains(Class<?> type);
/** 获取已注册的实例 */
<T> T get(Class<T> type) throws IllegalStateException;
/** 获取或返回默认值 */
<T> T getOrElse(Class<T> type, T other);
/** Scope 接口 —— 控制实例的生命周期 */
@FunctionalInterface
interface Scope {
void close(Class<?> type);
}
}
// BootstrapContext.java —— 只读查询接口
public interface BootstrapContext {
<T> T get(Class<T> type) throws IllegalStateException;
<T> T getOrElse(Class<T> type, T other);
}2. DefaultBootstrapContext 内部结构
java
// DefaultBootstrapContext.java
public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
// 核心存储:LinkedHashMap 保持注册顺序
private final Map<Class<?>, Object> registrations = new LinkedHashMap<>();
// 已解析的实例缓存(懒加载创建后缓存)
private final Map<Class<?>, Object> instances = new LinkedHashMap<>();
private final Map<Class<?>, Scope> scopes = new HashMap<>();
// 是否已关闭
private boolean closed = false;
}2.1 三个 Map 的分工
| Map | 类型 | 用途 | 何时写入 |
|---|---|---|---|
registrations | Map<Class<?>, Object> | 存储 InstanceSupplier<?> 或已解析的实例 | register() 时立即写入 |
instances | Map<Class<?>, Object> | 缓存已解析的单例实例 | get() 首次访问时懒加载写入 |
scopes | Map<Class<?>, Scope> | 存储每个注册项的 Scope | register(..., Scope) 时写入 |
2.2 LinkedHashMap 的选择原因
使用 LinkedHashMap 而非 HashMap 是因为:
- 顺序保证:注册顺序决定
get()的遍历优先级(虽然实际通过类型直接查找,但保留了诊断信息的可读性) - 迭代顺序一致:
close()遍历时按注册顺序逆序关闭,保证依赖关系正确
2.3 register() 实现
java
@Override
public <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier,
Scope scope) {
Assert.notNull(type, "Type must not be null");
Assert.notNull(instanceSupplier, "InstanceSupplier must not be null");
// 已关闭后禁止注册
if (this.closed) {
throw new IllegalStateException(
"BootstrapContext is closed");
}
// 如果已存在,允许覆盖(暂存旧值供 Scope 清理)
Object existing = this.registrations.put(type, instanceSupplier);
if (existing != null) {
// 清理旧 scope
Scope existingScope = this.scopes.get(type);
if (existingScope != null) {
existingScope.close(type);
}
}
// 存储 scope
if (scope != null) {
this.scopes.put(type, scope);
}
// 清除已缓存的实例(下次 get() 时重新创建)
this.instances.remove(type);
}3. SingletonScope vs PrototypeScope
Spring Boot 内置了两种 Scope:
java
// DefaultBootstrapContext 的内部类
static class SingletonScope implements BootstrapRegistry.Scope {
@Override
public void close(Class<?> type) {
// SingletonScope:关闭时什么也不做(实例由 BootstrapContext 管理)
}
}
static class PrototypeScope implements BootstrapRegistry.Scope {
@Override
public void close(Class<?> type) {
// PrototypeScope:关闭时清理原型实例
}
}3.1 get() 的差异
java
@Override
@SuppressWarnings("unchecked")
public <T> T get(Class<T> type) throws IllegalStateException {
// 1. 尝试从 instances 缓存获取
Object instance = this.instances.get(type);
if (instance != null) return (T) instance;
// 2. 查找 registration
Object registration = this.registrations.get(type);
if (registration == null) {
throw new IllegalStateException(
"'" + type.getName() + "' has not been registered yet");
}
// 3. 如果注册的是 InstanceSupplier,则调用它创建
if (registration instanceof InstanceSupplier) {
Scope scope = this.scopes.get(type);
InstanceSupplier<T> supplier = (InstanceSupplier<T>) registration;
instance = supplier.get(this);
// SingletonScope:缓存实例
if (scope == null || scope instanceof SingletonScope) {
this.instances.put(type, instance);
}
// PrototypeScope:不缓存(每次都新创建)
}
return (T) instance;
}3.2 Scope 对比
| Scope | 是否缓存实例 | 每次 get() 返回 | 典型场景 |
|---|---|---|---|
SingletonScope(默认) | ✅ 缓存 | 同一实例 | SpringApplication、Environment 等全局单例 |
PrototypeScope | ❌ 不缓存 | 新实例 | 每次需要新状态的工厂类 |
3.3 register() 不指定 scope 时的默认行为
java
// 不传 scope 的重载方法
public <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier) {
register(type, instanceSupplier, null); // scope = null → SingletonScope 语义
}4. get() 与 getOrElse() 的异常语义
4.1 get() — 找不到时抛异常
java
@Override
public <T> T get(Class<T> type) throws IllegalStateException {
Object instance = this.instances.get(type);
if (instance != null) return (T) instance;
Object registration = this.registrations.get(type);
if (registration == null) {
// ↑↑↑ 未注册时抛出 IllegalStateException
throw new IllegalStateException(
"'" + type.getName() + "' is not registered.");
}
if (registration instanceof InstanceSupplier) {
InstanceSupplier<T> supplier = (InstanceSupplier<T>) registration;
instance = supplier.get(this);
Scope scope = this.scopes.get(type);
if (scope == null || scope instanceof SingletonScope) {
this.instances.put(type, instance);
}
}
return (T) instance;
}4.2 getOrElse() — 找不到时返回默认值
java
@Override
@SuppressWarnings("unchecked")
public <T> T getOrElse(Class<T> type, T other) {
// 先尝试从 instances 缓存中查找
Object instance = this.instances.get(type);
if (instance != null) return (T) instance;
// 再从 registrations 中查找
Object registration = this.registrations.get(type);
if (registration == null) {
// ↑↑↑ 找不到时直接返回传入的默认值 other,不抛异常
return other;
}
// ... 后续创建逻辑与 get() 相同
}4.3 异常场景对比
| 场景 | get() | getOrElse() |
|---|---|---|
| 已注册且已缓存 | 返回实例 | 返回实例 |
| 已注册未创建 | 创建并返回 | 创建并返回 |
| 未注册 | 抛出 IllegalStateException | 返回默认值 other |
| 已关闭 | 抛出 IllegalStateException | 返回默认值 |
5. BootstrapRegistryInitializer.bootstrap() 回调时机
5.1 执行顺序
BootstrapRegistryInitializer 是最早被回调的 SPI 扩展点,在 run() 方法的最前面:
java
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
// 1. 创建 BootstrapContext(第 1 步)
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// ↑↑↑ 此时 bootstrapContext 已创建但尚未填充
// 2. 配置 Headless 模式
configureHeadlessProperty();
// 3. 获取 RunListeners 并发布 starting 事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext);
try {
// 4. 准备 Environment 阶段
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// ↑↑↑ bootstrapContext 传入
// ...
}
// ...
}5.2 createBootstrapContext() 内部
java
// SpringApplication.java
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 遍历所有 BootstrapRegistryInitializer,调用其 bootstrap(registry)
this.bootstrapRegistryInitializers.forEach(initializer ->
initializer.bootstrap(bootstrapContext));
return bootstrapContext;
}5.3 BootstrapRegistryInitializer 示例
java
// 自定义 BootstrapRegistryInitializer
public class MyBootstrapRegistryInitializer implements BootstrapRegistryInitializer {
@Override
public void bootstrap(BootstrapRegistry registry) {
// 在 Environment 准备之前注册自定义对象
registry.register(MyConfigLoader.class,
ctx -> new MyConfigLoader());
}
}5.4 执行时序图
run() 入口
│
├─ createBootstrapContext()
│ └─ BootstrapRegistryInitializer.bootstrap(registry) ← 最早回调
│ ├─ register(MyBean.class, supplier)
│ └─ register(MyConfig.class, supplier)
│
├─ configureHeadlessProperty()
│
├─ listeners.starting(bootstrapContext)
│ └─ ApplicationStartingEvent 发布
│
├─ prepareEnvironment()
│ ├─ Environment 准备
│ └─ EnvironmentPostProcessor 处理
│
├─ prepareContext()
│ ├─ ApplicationContextInitializer 执行
│ └─ ...
│
└─ refreshContext() ← 容器刷新,Bean 实例化6. BootstrapContext.close() 的清理逻辑
6.1 close() 实现
java
// DefaultBootstrapContext.java
@Override
public void close() {
// 标记已关闭
this.closed = true;
// 逆序遍历 scopes,调用每个 scope 的 close()
Iterator<Map.Entry<Class<?>, Scope>> iterator =
this.scopes.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Class<?>, Scope> entry = iterator.next();
try {
// 调用 Scope.close(type)
entry.getValue().close(entry.getKey());
} catch (Exception ex) {
// 记录日志,不阻止后续清理
logger.warn("Failed to close scope for " + entry.getKey(), ex);
}
iterator.remove();
}
// 清空所有缓存
this.registrations.clear();
this.instances.clear();
this.scopes.clear();
}6.2 清理流程
close()
│
├─ 1. closed = true (禁止后续 register/get)
│
├─ 2. 遍历 scopes(逆序)
│ ├─ SingletonScope.close(type) → 空操作
│ └─ PrototypeScope.close(type) → 清理原型实例
│
├─ 3. registrations.clear() → 清空所有注册项
├─ 4. instances.clear() → 清空所有缓存实例
└─ 5. scopes.clear() → 清空所有 scope6.3 close() 调用时机
close() 在 SpringApplication.run() 的 finally 块中被确保调用:
java
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// ...
try {
// ... 启动全流程 ...
return context;
} catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
} finally {
// ↑↑↑ 确保 BootstrapContext 在 run() 结束时被关闭
bootstrapContext.close();
}
}7. SpringFactoriesLoader 加载 BootstrapRegistryInitializer
7.1 独立 SPI 文件
在 Spring Boot 3.x 中,BootstrapRegistryInitializer 使用独立的 imports 文件,而非 spring.factories:
# META-INF/spring/org.springframework.boot.BootstrapRegistryInitializer.imports
com.example.MyBootstrapRegistryInitializer7.2 加载源码
java
// SpringApplication.java (构造函数)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 初始化 bootstrapRegistryInitializers
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// ...
}getSpringFactoriesInstances() 内部通过 ImportCandidates 加载:
java
// SpringFactoriesLoader 适配
private static final List<String> BOOTSTRAP_REGISTRY_INITIALIZER_CANDIDATES =
ImportCandidates.load(BootstrapRegistryInitializer.class, null)
.getCandidates();7.3 SPI 文件位置清单
| SPI 类型 | 文件位置 | 加载机制 |
|---|---|---|
BootstrapRegistryInitializer | META-INF/spring/org.springframework.boot.BootstrapRegistryInitializer.imports | ImportCandidates(3.x 新机制) |
ApplicationContextInitializer | META-INF/spring.factories | SpringFactoriesLoader(旧机制) |
ApplicationListener | META-INF/spring.factories | SpringFactoriesLoader(旧机制) |
SpringApplicationRunListener | META-INF/spring.factories | SpringFactoriesLoader(旧机制) |
8. 与 Spring Cloud Bootstrap Context 的关系
8.1 概念区分
Spring Boot BootstrapContext | Spring Cloud Bootstrap Context | |
|---|---|---|
| 所属框架 | Spring Boot 3.x 内置 | Spring Cloud(通常与 Spring Boot 2.x 配合) |
| 阶段 | SpringApplication.run() 最早期 | SpringApplication 启动前的独立上下文 |
| 用途 | 轻量级注册表,传递早期启动对象 | 加载远程配置(Spring Cloud Config Server) |
| 生命周期 | run() 执行期间存在,结束后关闭 | 独立的 ApplicationContext,bootstrap.yml 驱动 |
| 存储方式 | LinkedHashMap | Spring ApplicationContext(完整的 BeanFactory) |
8.2 互不冲突的原因
Spring Cloud 在 Spring Boot 3.x 中也调整了实现方式,不再创建独立的 bootstrap 上下文,而是通过 PropertySource 机制实现远程配置加载。因此:
- Spring Boot 3.x 的
BootstrapContext是启动引擎内部的轻量设施 - Spring Cloud 的远程配置 通过
EnvironmentPostProcessor在环境准备阶段加载
两者目标不同、阶段不同、实现不同,互不冲突。
总结
| 细节点 | 核心要点 |
|---|---|
① DefaultBootstrapContext 内部结构 | LinkedHashMap 存储 registrations / instances / scopes |
② SingletonScope vs PrototypeScope | Singleton 缓存实例,Prototype 每次新创建 |
③ get() vs getOrElse() | 未注册时抛 IllegalStateException vs 返回默认值 |
④ bootstrap() 回调时机 | createBootstrapContext() 中立即回调,早于任何 Environment 准备 |
⑤ close() 清理逻辑 | 逆序遍历 Scope → 清空三个 Map |
| ⑥ 独立 SPI 文件 | META-INF/spring/...BootstrapRegistryInitializer.imports |
| ⑦ 与 Spring Cloud 的关系 | 概念不同、阶段不同、互不冲突 |