SpringBoot的生命周期原理分析之一SpringBoot准备容器与环境
目录
1.2.3运行时环境创建、触发与 SpringApplication 绑定
1.SpringBootApplication准备
SpringBoot启动调用的run方法是一个重载的run方法,将启动的动作分为了两步:创建SpringApplication对象和启动SpringApplication。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
//***************run方法内部***************//
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
1.1SpringApplication创建
SpringApplication 构造方法不启动容器,只做启动前的核心准备工作:保存配置、判断应用类型、加载系统扩展器、加载监听器、定位入口类,为 run() 方法启动容器做好铺垫。SpringApplication的构造方法如下。
public class SpringApplication {
/**
* SpringApplication 构造方法(核心简化版)
* @param primarySources 主配置类(如启动类 XXXApplication)
*/
public SpringApplication(Class<?>... primarySources) {
// 1. 保存主配置源
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2. 推断Web环境(NONE/SERVLET/REACTIVE)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3. 加载并初始化所有 ApplicationContextInitializer
setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4. 加载并初始化所有 ApplicationListener
setListeners(getSpringFactoriesInstances(ApplicationListener.class));
// 5. 推断并设置主方法所在类
this.mainApplicationClass = deduceMainApplicationClass();
}
}
1.2.1保存主配置源
这一步将传入的启动类(如Application.class)存入集合,作为 Spring 容器根配置源。标记 Spring 从哪里开始扫描、解析配置,后续会从这里加载@SpringBootApplication、@Configuration等配置。
1.2.2推断Web环境
根据类路径是否存在特定类判断应用类型:有DispatcherServlet为SERVLET web 应用;有 DispatcherHandler为REACTIVE 响应式;都没有为非 Web 应用。
public static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null)
&& !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)) {
return REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return NONE;
}
}
return SERVLET;
}
1.2.3设置初始化器
// 从 spring.factories 中加载所有初始化器(SPI 服务发现)
List<ApplicationContextInitializer<?>> initializers = SpringFactoriesLoader.loadFactories(
ApplicationContextInitializer.class, classLoader);
// 设置到当前 SpringApplication 实例中
this.initializers = initializers;
这通过 Spring 内置的 SPI 机制,从类路径下META-INF/spring.factories文件中加载并实例化所有ApplicationContextInitializer 接口的实现类。ApplicationContextInitializer是 Spring 提供的容器扩展接口,其核心方法 initialize(ConfigurableApplicationContext context)用于在容器启动阶段对上下文进行定制。接口操作的对象是ConfigurableApplicationContext,它是 SpringBoot 最终使用的可配置型 IOC 容器,允许对环境、BeanDefinition、系统配置等进行修改。这些初始化器会在 IOC 容器创建之后、refresh刷新之前统一执行,能够对容器进行环境配置、Profile 激活、BeanDefinition 注册、属性设置等预处理,直接决定 IOC 容器的初始状态与后续行为,是 SpringBoot 实现自动配置与扩展的重要入口。设置初始化器的整体逻辑为:先通过 SPI 机制发现并加载所有实现类,将其存入集合统一管理,在 run 方法启动流程中,容器创建完成后遍历调用 initialize 方法完成对 IOC 容器的前置增强。
1.2.4设置监听器
这一步同样基于 SPI 机制,使用 SpringFactoriesLoader 从 META-INF/spring.factories 中加载并实例化所有 ApplicationListener 接口的实现类,是 Spring Framework 事件驱动模型的核心组成部分。ApplicationListener 本质是 观察者模式中的观察者,Spring 应用上下文在生命周期各个节点会发布对应的 ApplicationEvent 事件(被观察对象),实现了该接口的监听器会在事件触发时自动执行 onApplicationEvent 回调逻辑。这些监听器会在容器启动的全生命周期中监听并响应事件,例如环境准备、容器刷新、启动完成、启动失败等,用于实现日志输出、状态统计、上下文扩展等功能。设置监听器的逻辑为:通过 SPI 加载所有系统及自定义监听器实例,统一存入集合管理,在 run 方法执行过程中,由事件发布器在对应生命周期节点广播事件,触发监听器执行,从而实现 Spring 容器的事件驱动与解耦扩展,让容器生命周期与业务扩展逻辑分离
1.2.5确定主启动类
遍历线程栈,找到包含 main() 方法的类,以确定应用入口。
1.2.6扩展了解:SpringBoot的发展
SpringBoot2.x:SpringBoot1.x所有的配置源都被视为Object类型;SpringBoot2.x之后,引入了Set<Class<?>>类型主配置类(存放注解配置),同时,为了支持少部分XML配置使用场景,保留了一个Set<String>类型的集合。SpringBoot1.x中推断的方法还是在SpringApplication中直接实现的,并且只会在Servlet和None之间做选择;而SpringBoot2.x后Web类型推断的逻辑被单独抽取为一个静态方法。
SpringBoot2.4.x:引入了一个全新的API BootstrapRegistry,作为一个全新的对象容器,可以用于提前创建一些创建成本很高的对象,等到ApplicationContext需要时,直接共享给它。相关的初始化也留了一些扩展点。
1.2SpringBootApplication启动
public class SpringApplication {
/**
* SpringBoot 启动核心方法
* @param args 启动参数
* @return 容器上下文
*/
public ConfigurableApplicationContext run(String... args) {
// 1. 创建计时器,记录启动耗时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 初始化引导上下文、环境配置
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
//3.运行时环境创建、触发与 SpringApplication 绑定
ConfigurableEnvironment environment = prepareEnvironment(bootstrapContext, args);
// 4.允许Bean定义是否覆盖(默认关闭)
configureIgnoreBeanInfo(environment);
// 5. 打印Banner
Banner printedBanner = printBanner(environment);
// 5. 创建Spring容器(根据Web类型创建)
ConfigurableApplicationContext context = createApplicationContext();
// 6. 准备容器:设置环境、执行初始化器、注册主配置类
prepareContext(bootstrapContext, context, environment, printedBanner);
// *******刷新容器:扫描Bean、创建单例、自动配置****************
refreshContext(context);
// 7. 容器刷新后:执行Runner、启动完成回调
afterRefresh(context, args);
// 8. 停止计时器,输出启动日志
stopWatch.stop();
// 9. 返回容器
return context;
}
}
1.2.1启动计时与全局异常处理机制
启动方法首先开启StopWatch计时器用于统计启动耗时,同时建立全局异常捕获与分析机制,整个启动流程中发生的异常(如配置错误、类缺失、Bean 注册失败、端口冲突等)会被统一捕获,进行异常分类、日志格式化与友好提示,并最终终止启动流程,这是 SpringBoot 具备健壮启动诊断能力的基础。异常处理逻辑属于 run 方法的内部底层实现,在上述简化代码中未展示,核心为外层 try-catch 统一捕获启动异常。
1.2.2Bootstrap 引导上下文创建与早期扩展执行
启动流程会创建DefaultBootstrapContext引导上下文,这是 SpringBoot 2.4.x 新增的早期对象容器,在此阶段会通过 SPI 机制加载并执行所有BootstrapRegistryInitializer扩展点,该扩展点是整个应用最早的干预入口,可在无 IOC 容器时注册单例对象、配置中心客户端等早期组件,完成最前置的系统初始化。该步骤对应代码中createBootstrapContext()方法。
1.2.3运行时环境创建、触发与 SpringApplication 绑定
private ConfigurableEnvironment prepareEnvironment(DefaultBootstrapContext bootstrapContext, String[] args) {
// 1. 根据 Web 类型创建对应的 Environment 实现类
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2. 配置属性源:命令行参数、application.yml、系统变量、环境变量
configureEnvironment(environment, args);
// 3. 发布 ApplicationEnvironmentPreparedEvent 环境准备完成事件
listeners.environmentPrepared(bootstrapContext, environment);
return environment;
}
/***************************************/
private ConfigurableEnvironment getOrCreateEnvironment() {
// Servlet 环境 → StandardServletEnvironment
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
// 非 Web / Reactive → StandardEnvironment
return new StandardEnvironment();
}
执行prepareEnvironment方法完成运行时环境(Environment)的创建,SpringBoot 会根据当前应用类型,实例化 Environment 对应的落地实现类:Servlet 环境使用StandardServletEnvironment,Reactive 环境与非 Web 环境使用StandardEnvironment,两类实现类均会加载系统属性、环境变量、命令行参数、application.yml/properties配置源,并按照固定优先级完成属性合并,最终形成可全局使用的配置环境对象。该方法会同时将环境实例与SpringApplication完成绑定,让启动全流程共享同一套环境配置;环境创建过程会发布环境相关事件,支持外部扩展对环境进行动态修改与增强。
private SpringApplicationRunListeners getRunListeners(String[] args) {
// SPI 机制加载所有运行时监听器
List<SpringApplicationRunListener> listeners = SpringFactoriesLoader.loadFactories(
SpringApplicationRunListener.class, getClassLoader());
return new SpringApplicationRunListeners(listeners);
}
/************************************************/
public class EventPublishingRunListener implements SpringApplicationRunListener {
private final SpringApplication application;
private final ApplicationEventMulticaster multicaster;
// 环境准备完成 → 发布 ApplicationEnvironmentPreparedEvent
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
multicaster.multicastEvent(
new ApplicationEnvironmentPreparedEvent(application, args, environment));
}
// 上下文初始化完成 → 发布 ApplicationContextInitializedEvent
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
multicaster.multicastEvent(
new ApplicationContextInitializedEvent(application, args, context));
}
}
/**************SpringBoot事件启动体系*****************/
ApplicationStartingEvent // 启动开始
ApplicationEnvironmentPreparedEvent // 环境就绪
ApplicationContextInitializedEvent // 初始化器执行完毕
ApplicationPreparedEvent // 上下文准备完毕
ApplicationStartedEvent // 容器已启动
ApplicationReadyEvent // 可接收请求
在prepareEnvironment和prepareContext的内部底层,启动流程会加载并激活 SpringBoot 内置核心监听器。其中EventPublishingRunListener 是事件驱动的核心,它负责在启动的每个关键节点(环境准备、上下文准备、启动完成、启动失败)发布对应的SpringApplicationEvent事件;所有注册在SpringApplication中的ApplicationListener监听器会监听并响应这些事件,实现解耦式扩展,是 Spring Framework 观察者模式在启动阶段的核心体现。
依托EventPublishingRunListener,SpringBoot 在启动阶段触发一套完整的扩展事件体系,按执行顺序包括:ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationContextInitializedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent,自定义监听器监听任意事件即可实现启动阶段的扩展干预,无需侵入核心启动逻辑,每个关键启动节点都会触发对应的事件。
1.2.4环境配置完成与系统基础配置
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
// 判断系统属性中是否已指定 java.beans.ignoreBeanInfo
if (System.getProperty("java.beans.ignoreBeanInfo") == null) {
// 从环境配置中获取 spring.beaninfo.ignore 属性值
String ignore = environment.getProperty("spring.beaninfo.ignore");
// 如果配置不为空,则设置到系统属性中
if (StringUtils.hasText(ignore)) {
System.setProperty("java.beans.ignoreBeanInfo", ignore);
}
}
}
环境对象创建完成后,会执行configureIgnoreBeanInfo等系统基础配置,关闭无需使用的 BeanInfo 缓存以优化性能,同时完成环境与启动上下文的最终绑定,确保后续 Banner 打印、上下文准备等环节都能读取到完整的配置信息。该步骤对应代码configureIgnoreBeanInfo(environment)方法。
2. IOC容器准备
1.1Banner打印
private Banner printBanner(ConfigurableEnvironment environment) {
BannerMode mode = environment.getProperty("spring.banner.mode", BannerMode.class, BannerMode.CONSOLE);
Banner banner = getBanner();
if (mode == BannerMode.CONSOLE) {
banner.printBanner(environment, this.mainApplicationClass, System.out);
}
return banner;
}
在 IOC 容器创建之前,SpringBoot 会根据当前环境配置打印启动 Banner,用于标识应用启动状态,支持文本、图片或关闭模式。这一步仅为可视化展示,不影响容器逻辑,但可通过自定义 Banner 实现应用标识扩展。
1.2IOC容器的创建
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
if (this.webApplicationType == WebApplicationType.SERVLET) {
contextClass = AnnotationConfigServletWebServerApplicationContext.class;
} else if (this.webApplicationType == WebApplicationType.REACTIVE) {
contextClass = AnnotationConfigReactiveWebServerApplicationContext.class;
} else {
contextClass = AnnotationConfigApplicationContext.class;
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
根据启动前推断的 WebApplicationType,实例化对应类型的 ConfigurableApplicationContext,这是 SpringBoot 的真正 IOC 容器。不同环境对应不同容器实现,容器本身仅被创建,尚未加载配置、Bean 或执行初始化。
1.3IOC容器的初始化
IOC 容器创建后的核心初始化流程,包含容器后置处理、执行 ApplicationContextInitializer、获取并加载配置源,是启动阶段干预 IOC 容器的核心扩展点。
private void prepareContext(DefaultBootstrapContext bootstrapContext,
ConfigurableApplicationContext context,
ConfigurableEnvironment environment,
Banner printedBanner) {
// 1. 将环境绑定到IOC容器
context.setEnvironment(environment);
// ====================== 初始化第一部分:IOC容器后置处理 ======================
postProcessApplicationContext(context);
// ====================== 初始化第二部分:执行所有ApplicationContextInitializer ======================
applyInitializers(context);
// 发布上下文准备完成事件
listeners.contextPrepared(context);
// ====================== 初始化第三部分:获取并加载主配置源 ======================
Set<Object> sources = getAllSources();
loadIntoContext(context, sources);
// 发布上下文已加载事件
listeners.contextLoaded(context);
}
/**************************后置处理器**************************/
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
// 设置Bean名称生成器
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator);
}
// 设置类路径资源加载器
if (this.resourceLoader != null) {
context.setResourceLoader(this.resourceLoader);
}
}
/************************执行所有初始化器*************************************/
protected void applyInitializers(ConfigurableApplicationContext context) {
// 遍历并执行所有初始化器,直接操作IOC容器
for (ApplicationContextInitializer<?> initializer : getInitializers()) {
initializer.initialize(context);
}
}
/**************************获取配置源*************************************************/
// 获取所有配置源(主启动类、配置类等)
public Set<Object> getAllSources() {
return new LinkedHashSet<>(this.primarySources);
}
// 将配置源加载到IOC容器
private void loadIntoContext(ConfigurableApplicationContext context, Set<Object> sources) {
if (context instanceof BeanDefinitionRegistry) {
// 注册配置类(启动类)到容器,作为扫描与配置入口
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context;
for (Object source : sources) {
// 注册BeanDefinition
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
reader.register((Class<?>) source);
}
}
}
prepareContext是启动阶段最重要的容器干预扩展点,会将已创建的 Environment 绑定到上下文(注意:与1.2.3的绑定并非同一个操作:在prepareEnvironment阶段将环境实例绑定到SpringApplication是为了保证整个启动流程都能使用同一套配置;而在prepareContext阶段将环境绑定到ApplicationContext,是为了让 IOC 容器内部的 Bean、配置注入等运行时逻辑可以使用该环境,二者作用对象、使用阶段和服务目标不同,但共用同一个 Environment 实例。),同时批量执行所有ApplicationContextInitializer扩展点,通过其initialize方法可直接对ConfigurableApplicationContext进行定制化修改,完成环境增强、BeanDefinition 注册、Profile 激活等操作,是外部扩展影响 SpringBoot 启动行为的核心入口。
【注】:1.《SpringBoot源码解读与原理分析》LinkedBear著第六章阅读笔记,如果错误,欢迎指正
更多推荐
所有评论(0)