当前位置: 首页 > backend >正文

深入剖析Spring Boot应用启动全流程

目录

前言

启动流程概览

一、第一阶段:初始化SpringApplication

二、第二阶段:运行SpringApplication

三、第三阶段:环境准备

四、第四阶段:创建应用上下文

五、第五阶段:准备应用上下文

六、第六阶段:刷新应用上下文(核心)

七、第七阶段:启动后处理

启动流程图解

Spring Boot启动流程与自动装配的联系

总结


前言

        Spring Boot以其"开箱即用"的特性大大简化了Spring应用的开发部署流程。只需一个main方法和一个简单的SpringApplication.run()调用,我们的应用就能快速启动。但这背后究竟发生了什么?本文将深入剖析Spring Boot应用的完整启动流程,带你理解从点击"运行"到应用完全就绪的每一个关键步骤。


启动流程概览

Spring Boot的启动过程可以概括为以下几个核心阶段:

  1. 初始化SpringApplication实例

  2. 运行SpringApplication

  3. 准备环境设置

  4. 创建应用上下文

  5. 刷新应用上下文(核心)

  6. 执行Runner接口实现

下面我们详细分析每个阶段的具体工作。

一、第一阶段:初始化SpringApplication

        当我们调用SpringApplication.run(Application.class, args)时,首先会初始化一个SpringApplication实例

//代码示例
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);
}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);
}

在SpringApplication的构造函数中,会进行以下关键操作:

  1. 推断应用类型:根据类路径下的依赖判断是SERVLET应用(Spring MVC)、REACTIVE应用(WebFlux)还是普通应用

  2. 加载应用上下文初始化器:通过SpringFactoriesLoader从META-INF/spring.factories加载ApplicationContextInitializer

  3. 加载应用监听器:同样通过SpringFactoriesLoader加载ApplicationListener

  4. 推断主配置类:根据堆栈信息找到包含main方法的类

二、第二阶段:运行SpringApplication

run方法是整个启动流程的核心:

public ConfigurableApplicationContext run(String... args) {// 1. 创建启动计时器StopWatch stopWatch = new StopWatch();stopWatch.start();// 2. 初始化默认应用上下文ConfigurableApplicationContext context = null;// 3. 配置headless属性configureHeadlessProperty();// 4. 获取SpringApplicationRunListenersSpringApplicationRunListeners listeners = getRunListeners(args);// 5. 发布应用开始启动事件listeners.starting();try {// 6. 准备环境ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 7. 打印BannerBanner printedBanner = printBanner(environment);// 8. 创建应用上下文context = createApplicationContext();// 9. 准备应用上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 10. 刷新应用上下文(核心步骤)refreshContext(context);// 11. 上下文刷新后处理afterRefresh(context, applicationArguments);// 12. 停止计时器stopWatch.stop();// 13. 发布应用启动完成事件if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);// 14. 调用ApplicationRunner和CommandLineRunnercallRunners(context, applicationArguments);} catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);} catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;
}

三、第三阶段:环境准备

prepareEnvironment()方法负责准备应用运行环境:

  1. 创建环境对象:根据应用类型创建StandardEnvironment或StandardServletEnvironment

  2. 配置环境:配置PropertySources和Profiles

  3. 发布环境准备事件:通过EnvironmentPostProcessorApplicationListener处理

  4. 绑定环境到SpringApplication

  5. 转换配置:将命令行参数转换为PropertySource

  6. 处理ConfigurationProperties:验证和绑定@ConfigurationProperties

四、第四阶段:创建应用上下文

createApplicationContext()根据应用类型创建对应的应用上下文:

  • Servlet应用:AnnotationConfigServletWebServerApplicationContext

  • Reactive应用:AnnotationConfigReactiveWebServerApplicationContext

  • 普通应用:AnnotationConfigApplicationContext

五、第五阶段:准备应用上下文

prepareContext()方法准备创建好的应用上下文:

  1. 设置环境

  2. 后处理上下文:调用ApplicationContextInitializer

  3. 发布上下文准备事件

  4. 注册SpringBootBanner

  5. 设置资源加载器和类加载器

  6. 注册Bean定义

    • 注册主配置类(@SpringBootApplication标注的类)

    • 注册命令行参数Bean

    • 注册Banner Bean

六、第六阶段:刷新应用上下文(核心)

refreshContext()调用的是AbstractApplicationContext的refresh()方法,这是Spring容器的核心生命周期方法:

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 1. 准备刷新上下文prepareRefresh();// 2. 获取刷新后的BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 3. 准备BeanFactory使用上下文prepareBeanFactory(beanFactory);try {// 4. 允许BeanFactory的后处理postProcessBeanFactory(beanFactory);// 5. 调用BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);// 6. 注册BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 7. 初始化MessageSourceinitMessageSource();// 8. 初始化事件广播器initApplicationEventMulticaster();// 9. 初始化特殊Bean(由子类实现)onRefresh();// 10. 注册监听器registerListeners();// 11. 完成BeanFactory初始化,实例化所有非懒加载单例finishBeanFactoryInitialization(beanFactory);// 12. 完成刷新,发布上下文刷新事件finishRefresh();} catch (BeansException ex) {// 13. 销毁已创建的单例BeandestroyBeans();// 14. 重置激活标志cancelRefresh(ex);throw ex;} finally {// 15. 重置Spring核心中的公共内省缓存resetCommonCaches();}}
}

对于Spring Boot来说,onRefresh()方法尤为重要,这里会创建嵌入式Web服务器:

protected void onRefresh() {super.onRefresh();try {createWebServer(); // 创建Tomcat、Jetty或Undertow服务器} catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}

七、第七阶段:启动后处理

刷新完成后,Spring Boot会执行一些后处理操作:

  1. 调用ApplicationRunner和CommandLineRunner:执行应用中定义的Runner实现

  2. 发布应用就绪事件:ApplicationReadyEvent,表示应用已完全启动

  3. 启动完成:此时应用已完全就绪,可以处理请求

启动流程图解

Spring Boot启动流程与自动装配的联系

        1.核心关系:自动装配是启动流程的关键环节

        简单来说,自动装配是Spring Boot启动流程中的一个核心子过程。没有启动流程提供的环境、上下文和机制,自动装配无法工作;而没有自动装配,Spring Boot的启动就失去了"智能"和"自动化"的特性,退回到了传统Spring应用的繁琐配置模式。

        2.自动装配的具体执行时机

        自动装配发生在应用上下文刷新阶段,具体在refreshContext()方法中的invokeBeanFactoryPostProcessors(beanFactory)步骤:

public void refresh() throws BeansException, IllegalStateException {// ... 前面的步骤invokeBeanFactoryPostProcessors(beanFactory); // 自动装配在这里发生!// ... 后续步骤
}


总结

        Spring Boot的启动流程是一个精心设计的过程,它将传统的XML配置转换为基于Java的自动配置,通过条件化装配和自动发现机制,极大地简化了Spring应用的开发和部署。理解这个流程不仅有助于我们更好地使用Spring Boot,还能在遇到问题时快速定位和解决。

        整个过程体现了Spring Boot的核心设计理念:约定优于配置自动装配微内核架构。通过事件监听机制和扩展点设计,Spring Boot在保持简洁性的同时,也提供了极大的灵活性和可扩展性。

http://www.xdnf.cn/news/18510.html

相关文章:

  • 第七章 利用Direct3D绘制几何体
  • flink常见问题之非法配置异常
  • Hive Metastore和Hiveserver2启停脚本
  • jetson ubuntu 打不开 firefox和chromium浏览器
  • Python 实战:内网渗透中的信息收集自动化脚本(2)
  • 嵌入式LINUX——————网络TCP
  • Mysql InnoDB 底层架构设计、功能、原理、源码系列合集【六、架构全景图与最佳实践】
  • ArcGIS Pro 安装路径避坑指南:从崩溃根源到规范实操(附问题修复方案)
  • 在 CentOS 7 上搭建 OpenTenBase 集群:从源码到生产环境的全流程指南
  • SpringMVC相关自动配置
  • 第四十三天(JavaEE应用ORM框架SQL预编译JDBCMyBatisHibernateMaven)
  • 算法训练营day60 图论⑩ Bellman_ford 队列优化算法、判断负权回路、单源有限最短路
  • Vue 3 useModel vs defineModel:选择正确的双向绑定方案
  • [特殊字符] 在 Windows 新电脑上配置 GitHub SSH 的完整记录(含坑点与解决方案)
  • 简单留插槽的方法
  • 生成一个竖直放置的div,宽度是350px,上面是标题固定高度50px,下面是自适应高度的div,且有滚动条
  • 航空复杂壳体零件深孔检测方法 - 激光频率梳 3D 轮廓检测
  • FFMPEG相关解密,打水印,合并,推流,
  • 鸿蒙中Snapshot分析
  • Vue3+ElementPlus倒计时示例
  • 应用服务器和数据库服务器的区别
  • 机器学习案例——预测矿物类型(数据处理部分)
  • [CISCN2019 华北赛区 Day1 Web5]CyberPunk
  • `sudo apt update` 总是失败
  • Linux问答题:调优系统性能
  • 李宏毅NLP-12-语音分类
  • 基于Labview的旋转机械AI智能诊断系统
  • 2015-2018年咸海流域1km归一化植被指数8天合成数据集
  • html-docx-js 导出word
  • Linux问答题:归档和传输文件