在上一篇侦听器的设计和实现中,我们引入了 Spring Boot 启动状态机(如图1),如果要用一句话对 Spring Boot 启动阶段的工作用一句话进行概述,那么这句话一定是 完成对 Spring Context(运行时上下文) 的初始化,从状态机中,我们能看到上下文准备完成和上下文加载完成两个阶段,而这两个阶段所操作的核心对象,就是本篇的主角:Context。
Context这个词频繁出现在各类编程框架和系统设计之中:Android 有 android.content.Context,Go 标准库有 context.Context,Servlet 规范有 ServletContext,Spring 有 ApplicationContext,各类测试框架也几乎无一例外地存在某种 TestContext。这个命名并非巧合,而是计算机科学中一个有着深刻来源的设计概念。
“Context”一词来自于拉丁语 contextus,意为”连接在一起”,在自然语言中,”上下文”是理解一句话含义的必要背景信息。计算机科学借用了这个概念,用来描述一段代码或一个组件在运行时所处的完整环境状态,即”这段代码运行时,它所需要知道和依赖的一切”。
这一概念最早在操作系统领域得到系统性应用。CPU 在切换进程时,需要保存当前进程的寄存器状态、程序计数器、内存映射等信息,以便稍后恢复执行,这个被保存和恢复的完整状态快照,就被称为”进程上下文”(Process Context)。”上下文切换”(Context Switch)这个说法也由此而来。这个思想随后被延伸到更高层的软件设计中:当一个框架需要在运行时为其托管的组件提供统一的访问入口,使组件无需关心底层的环境细节,只需向这个”环境对象”发起请求时,这个对象自然而然地就被命名为 Context。
因此,在各类框架中频繁出现 Context 这个类名,其背后的共同意图是一致的:将运行时所需的状态、资源与服务聚合为一个统一的访问入口,对使用者屏蔽底层环境的复杂性。
1.2 Spring Context 承载的核心能力
理解了 Context 命名的本质,再来看 Spring 的 ApplicationContext 就会豁然开朗。Spring 框架需要为其托管的所有 Bean 提供一个统一的运行环境,这个环境需要能够回答组件在运行时可能提出的所有问题: “我需要某个依赖,去哪里找?”、”我需要读取一个配置文件,怎么访问?”、”某件事情发生了,谁需要被通知?”、”用户发来的消息应该用哪种语言回复?”
Spring 选择将这些能力通过接口继承的方式组合进 ApplicationContext,而不是全部自己实现。查看其接口声明,可以清晰地看到这一设计意图:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver { // ...}
ApplicationContext 同时继承了六个接口,每一个接口都对应着运行时环境中一类独立的关切(Concern),六者共同构成了一个 Spring 应用运行所需的完整上下文能力:
|
|
|
|
|
|
|
Bean 的注册、查找、依赖注入;这是 IoC 容器最核心的职责,也是 BeanFactory 体系的根本
|
|
|
|
支持父子 Context 结构,子 Context 可访问父 Context 中的 Bean,反之不能;这为 Web 应用中 Root Context 与 Servlet Context 的隔离提供了基础
|
|
|
|
提供对 Environment 对象的访问,Environment 聚合了所有配置属性(application.properties、环境变量、命令行参数等)以及激活的 Profile 信息
|
|
|
|
以统一的方式访问各类文件资源,无论资源位于类路径(classpath:)、文件系统(file:)还是远端 URL,对调用方完全透明
|
|
ApplicationEventPublisher
|
|
向容器内所有已注册的 ApplicationListener 发布事件,实现组件间的解耦通信;Spring Boot 启动过程中的各类生命周期事件正是通过此机制传播的
|
|
|
|
解析带有 locale 信息的消息文本,支持多语言国际化(i18n)场景
|
Spring 的上下文接口体系在 ApplicationContext 的基础上进一步分层,以区分”只读访问”与”可配置操作”这两种不同的使用场景:
BeanFactory └── HierarchicalBeanFactory └── ApplicationContext ← 只读:访问组件、资源、事件、环境 └── ConfigurableApplicationContext ← 可写:refresh / close / 注册钩子 └── ConfigurableWebApplicationContext ← Web 扩展:绑定 ServletContext
ApplicationContext 本身被设计为只读接口,即应用运行期间,外部代码只能通过它查询和访问容器内的信息,而不能修改容器状态。真正的生命周期操作由 ConfigurableApplicationContext 负责,它增加了以下关键能力:
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { // 刷新容器:加载 Bean 定义、初始化所有单例 Bean,是容器启动的核心方法 voidrefresh() throws BeansException, IllegalStateException; // 关闭容器:销毁所有单例 Bean,释放资源 voidclose(); // 注册 JVM 关闭钩子,确保 JVM 退出时容器能优雅关闭 voidregisterShutdownHook(); // ...}
SpringApplication.run 方法的返回类型正是 ConfigurableApplicationContext,框架在启动时需要调用 refresh() 完成容器初始化,在关闭时调用 close() 完成资源释放,这些都是只有框架自身才需要的”写”操作,对普通应用代码暴露 ApplicationContext 的只读视图已经足够。
理解了 ApplicationContext 接口所定义的能力边界之后,再来看它的实现类。Spring 提供了多种 ApplicationContext 的具体实现,以应对不同的部署场景和配置方式。这些实现类并非各自独立,而是形成了一棵清晰的继承树,树根是 AbstractApplicationContext。
AbstractApplicationContext 以模板方法模式(Template Method Pattern)定义了上下文生命周期的标准流程,其中最核心的是 refresh() 方法。它将容器启动拆分为十几个固定步骤,子类只需实现其中少数几个抽象方法(如 refreshBeanFactory()),即可获得完整的生命周期管理能力。这一骨架设计使得各场景下的具体实现可以专注于自身特有的逻辑(如如何加载 Bean 定义、如何启动 Web 服务器),而无需重复实现通用流程,refresh() 方法的详细分析将在后续章节展开。
AbstractApplicationContext └── GenericApplicationContext ├── AnnotationConfigApplicationContext └── GenericWebApplicationContext ├── ServletWebServerApplicationContext │ └── AnnotationConfigServletWebServerApplicationContext └── ReactiveWebServerApplicationContext └── AnnotationConfigReactiveWebServerApplicationContext
图2 Spring Application Context 类图
按照配置方式和应用场景,可将这些实现类分为以下四类:
通过 XML 配置文件加载 Bean 定义,适用于 Spring Boot 普及之前的传统 Spring 应用:
|
|
|
|
ClassPathXmlApplicationContext
|
|
|
FileSystemXmlApplicationContext
|
|
|
|
Web 环境下从 XML 文件加载配置,常用于传统 Spring MVC 应用
|
通过 @Configuration 类和注解扫描加载 Bean 定义,是现代 Spring Boot 应用的主流方式,也是本系列文章重点分析的对象:
|
|
|
|
AnnotationConfigApplicationContext
|
|
|
AnnotationConfigServletWebServerApplicationContext
|
适用于 Servlet(传统 MVC)Web 应用,Spring Boot 在 Web 场景下的默认选择
|
|
AnnotationConfigReactiveWebServerApplicationContext
|
|
提供灵活、可编程式注册 Bean 的通用实现,是注解配置上下文和 Web 上下文的公共父类:
|
|
|
|
GenericApplicationContext
|
最通用的上下文实现,持有一个 DefaultListableBeanFactory,支持以编程方式注册 Bean 定义
|
|
GenericWebApplicationContext
|
GenericApplicationContext 的 Web 扩展,持有对 ServletContext 的引用
|
在通用 Web 上下文的基础上,额外负责嵌入式 Web 服务器(如 Tomcat、Netty)的创建和生命周期管理:
|
|
|
|
ServletWebServerApplicationContext
|
在 refresh() 过程中创建并启动嵌入式 Servlet 服务器(默认 Tomcat)
|
|
ReactiveWebServerApplicationContext
|
在 refresh() 过程中创建并启动嵌入式响应式服务器(默认 Netty)
|
在第 2 章罗列的实现类中,有三个”注解配置”类是 Spring Boot 在不同场景下实际使用的核心实现。面对多种选择,Spring Boot 在运行时究竟会实例化哪一个?答案由两个串联的步骤决定:先推断应用类型(WebApplicationType),再按类型创建对应的 Context。
3.1 第一步:推断 WebApplicationType
WebApplicationType 是一个枚举,定义了 Spring Boot 所能运行的三种应用形态:
|
|
|
|
|
|
|
|
基于 Servlet 的传统 Web 应用,启动嵌入式 Servlet 服务器(默认 Tomcat)
|
|
|
基于响应式的 Web 应用,启动嵌入式响应式服务器(默认 Netty)
|
推断发生在 SpringApplication 的构造函数中,仅执行一次:
// SpringApplication.javapublicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // ... this.properties.setWebApplicationType(WebApplicationType.deduce()); // ← 推断 // ...}
WebApplicationType.deduce() 的逻辑分为两层:SPI 扩展推断 和 内置兜底逻辑。
Spring Boot 4.x 将推断逻辑从核心包中剥离,改由各 Web 模块以 SPI 的方式自行注册。WebApplicationType.deduce() 通过 SpringFactoriesLoader 加载所有 WebApplicationType.Deducer 实现,依次询问,取第一个非 null 的结果:
// WebApplicationType.javapublicstatic WebApplicationType deduce() { for (Deducer deducer : SpringFactoriesLoader.forDefaultResourceLocation().load(Deducer.class)) { WebApplicationType deduced = deducer.deduceWebApplicationType(); if (deduced != null) { return deduced; // 取第一个明确结论 } } return isServletApplication() ? SERVLET : NONE; // 兜底逻辑}
目前注册了两个 Deducer,它们分别由 spring-boot-webmvc 和 spring-boot-webflux 模块通过各自的 META-INF/spring.factories 提供:
WebMvcWebApplicationTypeDeducer(@Order(10),优先级更高)
// spring-boot-webmvc 模块提供@Order(10)class WebMvcWebApplicationTypeDeducer implements WebApplicationType.Deducer { private static final String[] INDICATOR_CLASSES = { "jakarta.servlet.Servlet", "org.springframework.web.servlet.DispatcherServlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; @Override public WebApplicationType deduceWebApplicationType() { for (String indicatorClass : INDICATOR_CLASSES) { if (!ClassUtils.isPresent(indicatorClass, null)) { return null; // 缺少任一类,放弃判断 } } return WebApplicationType.SERVLET; }}
WebFluxWebApplicationTypeDeducer(@Order(20),优先级较低)
// spring-boot-webflux 模块提供@Order(20)class WebFluxWebApplicationTypeDeducer implements WebApplicationType.Deducer { private static final String[] INDICATOR_CLASSES = { "reactor.core.publisher.Mono", "org.springframework.web.reactive.DispatcherHandler" }; @Override public WebApplicationType deduceWebApplicationType() { for (String indicatorClass : INDICATOR_CLASSES) { if (!ClassUtils.isPresent(indicatorClass, null)) { return null; } } return WebApplicationType.REACTIVE; }}
两个 Deducer 的判断逻辑完全对称:检查自身模块的标志性类是否存在于当前类路径。若某个类缺失,则返回 null 表示”无法判断”,将机会留给下一个 Deducer;若所有标志类均存在,则返回对应的 WebApplicationType。
若所有 Deducer 均返回 null(即没有任何 Web 模块在类路径中),则执行兜底逻辑:检测 jakarta.servlet.Servlet 和 ConfigurableWebApplicationContext 是否同时存在。满足则返回 SERVLET,否则返回 NONE。这一逻辑主要是为了兼容那些手动引入 Servlet API 但未依赖 Spring MVC 的边缘场景。
|
|
|
|
仅含 spring-boot-starter(无 Web)
|
|
|
含 spring-boot-starter-web(spring-boot-webmvc 模块)
|
|
|
仅含 spring-boot-starter-webflux(无 Servlet 依赖)
|
|
|
同时含 spring-boot-starter-web 和 spring-boot-starter-webflux
|
SERVLET(MVC 的 @Order(10) 优先于 WebFlux 的 @Order(20) 先被返回)
|
WebApplicationType 确定之后,Context 的实例化发生在 SpringApplication.run() 内部,由 createApplicationContext() 负责:
// SpringApplication.javaprotected ConfigurableApplicationContext createApplicationContext() { ConfigurableApplicationContext context = this.applicationContextFactory .create(this.properties.getWebApplicationType()); // ← 按类型创建 Assert.state(context != null, "ApplicationContextFactory created null context"); return context;}
applicationContextFactory 的默认实现是 DefaultApplicationContextFactory,它同样通过 SpringFactoriesLoader 加载所有注册的 ApplicationContextFactory,并将 WebApplicationType 交给每一个工厂,取第一个非 null 的结果:
// DefaultApplicationContextFactory.java@Overridepublic ConfigurableApplicationContext create(WebApplicationType webApplicationType) { return getFromSpringFactories(webApplicationType, ApplicationContextFactory::create, this::createDefaultApplicationContext); // 有兜底}private <T> T getFromSpringFactories(WebApplicationType webApplicationType, BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) { for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, ...)) { T result = action.apply(candidate, webApplicationType); if (result != null) { return result; } } return defaultResult.get(); // 兜底}
spring-boot-web-server 模块通过 META-INF/spring.factories 注册了两个工厂实现:
ServletWebServerApplicationContextFactory
@Overridepublic ConfigurableApplicationContext create(WebApplicationType webApplicationType) { // 只响应 SERVLET 类型 return (webApplicationType != WebApplicationType.SERVLET) ? null : createContext();}private ConfigurableApplicationContext createContext() { if (!AotDetector.useGeneratedArtifacts()) { return new AnnotationConfigServletWebServerApplicationContext(); // 常规模式 } return new ServletWebServerApplicationContext(); // AOT 预编译模式}
ReactiveWebServerApplicationContextFactory
@Overridepublic ConfigurableApplicationContext create(WebApplicationType webApplicationType) { // 只响应 REACTIVE 类型 return (webApplicationType != WebApplicationType.REACTIVE) ? null : createContext();}private ConfigurableApplicationContext createContext() { if (!AotDetector.useGeneratedArtifacts()) { return new AnnotationConfigReactiveWebServerApplicationContext(); // 常规模式 } return new ReactiveWebServerApplicationContext(); // AOT 预编译模式}
若两个工厂均返回 null(即 WebApplicationType 为 NONE),则 DefaultApplicationContextFactory 的兜底逻辑生效,直接 new AnnotationConfigApplicationContext()(AOT 模式下为 GenericApplicationContext)。
|
|
|
|
|
|
AnnotationConfigApplicationContext
|
GenericApplicationContext
|
|
|
AnnotationConfigServletWebServerApplicationContext
|
ServletWebServerApplicationContext
|
|
|
AnnotationConfigReactiveWebServerApplicationContext
|
ReactiveWebServerApplicationContext
|
如果自动推断的结果不符合需求(例如类路径同时存在 MVC 和 WebFlux,但希望以响应式模式运行),可以通过以下两种方式强制指定:
# application.propertiesspring.main.web-application-type=reactive
SpringApplication app = new SpringApplication(MyApplication.class);app.setWebApplicationType(WebApplicationType.REACTIVE);app.run(args);
spring.main.web-application-type 属性会在 Environment 准备完成后,通过 Binder 回绑到 SpringApplication 的内部属性对象,覆盖构造函数中 deduce() 写入的推断值,在 createApplicationContext() 被调用之前生效。
SpringApplication 构造函数 └── WebApplicationType.deduce() ├── 加载所有 Deducer(SPI) │ ├── WebMvcWebApplicationTypeDeducer @Order(10) │ │ 检测: jakarta.servlet.Servlet │ │ DispatcherServlet │ │ ConfigurableWebApplicationContext │ │ → 全部存在则返回 SERVLET │ └── WebFluxWebApplicationTypeDeducer @Order(20) │ 检测: reactor.core.publisher.Mono │ DispatcherHandler │ → 全部存在则返回 REACTIVE └── 兜底:有 Servlet API → SERVLET,否则 → NONESpringApplication.run() └── [可选] spring.main.web-application-type 覆盖推断值 └── createApplicationContext() └── DefaultApplicationContextFactory.create(webApplicationType) ├── 加载所有 ApplicationContextFactory(SPI) │ ├── ServletWebServerApplicationContextFactory │ │ SERVLET → AnnotationConfigServletWebServerApplicationContext │ └── ReactiveWebServerApplicationContextFactory │ REACTIVE → AnnotationConfigReactiveWebServerApplicationContext └── 兜底(NONE)→ AnnotationConfigApplicationContext