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

springboot启动流程

springboot启动分为两大步:1.创建SpringApplication对象。2.执行这个对象的run方法。

先看第一步做了什么事:

1.创建SpringApplication对象:

这其实就是一个构造器。

重点描述这几个部分:


1.确定应用程序的类型,

方法判断当前应用程序的容器,默认使用的是Servlet 容器,除了servlet之外,还有NONE 和 REACTIVE (响应式编程);

2.加载 ApplicationContextInitializer

这里加载的初始化器是springboot自带初始化器,从从 META-INF/spring.factories 配置文件中加载的,那么这个文件在哪呢?自带有2个,分别在源码的jar包的 spring-boot-autoconfigure 项目 和 spring-boot 项目里面各有一个。

在这里我们也可以自定义初始化器,只要实现这个接口就可以。

3.加载 ApplicationListener

载监听器也是从 META-INF/spring.factories 配置文件中加载的

4.设置应用程序的主类

deduceMainApplicationClass(); 这个方法是找到main方法所在的类,为后面的扫描包作准备。

至此第一部分结束,SpringApplication对象创建成功

2.执行SpringApplication对象的run方法

public ConfigurableApplicationContext run(String... args) {// stopwatch的中文翻译是秒表的意思// 创建一个秒表用于记录容器的启动时间StopWatch stopWatch = new StopWatch();// 秒表开启记时stopWatch.start();// applicationContext的上下文对象ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();/**第一步:加载spring.factories文件中的所有SpringApplicationRunListener对象这个对象是事件发布者,他在springBoot启动的不同阶段会发布不同的事件SpringApplicationRunListener接口只有一个实现类EventPublishingRunListener**/SpringApplicationRunListeners listeners = getRunListeners(args);/**第二步:run方法刚开始执行的事件广播即,触发事件ApplicationStartingEvent,所有监听了事件ApplicationStartingEvent的ApplicationListener都会在此时执行**/listeners.starting();try {/**第三步:创建并配置当前应用将要使用的环境1. 加载命令行2. 命令行中可能存在指定环境变量的参数--spring.active.profile=dev,所以需要传入参数applicationArguments3. 在创建环境完成够需要发布事件environmentPrepared,所以需要传入参数listeners**/ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);/**第四步:打印banner信息**/Banner printedBanner = printBanner(environment);/**第五步:创建applicationContext基于反射技术,根据应用类型创建了applicationContext对象,但是并没有设置applicationContext的属性到了这一步中context中有一个environment属性,但是其不是springApplication对象中的environment属性// environment.getProperty("test.string") = test// context.getEnvironment.getProperty("test.string") = null**/context = createApplicationContext();/**第六步:创建异常报告器1. 获取定义在spring.factories中的SpringBootExceptionReporter类的对象2. SpringBootExceptionReporter接口只有一个实现类FailureAnalyzers3. FailureAnalyzers会获取spring.factories定义的FailureAnalyzer类的对象  **/exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);/**第七步:初始化容器对象(初始化applicationContext)1.将第三步创建的环境设置到applicationContext里面去在这一步之后applicationContext中的environment等于springApplication对象中的environment2.调用ApplicationContextInitailizer接口applyInitializers(context);3.发布ApplicationContextInializer执行完毕事件 ApplicationContextInitializedEventlisteners.contextPrepared(context);4.输出容器启动的前两条日志// 2023-08-26 20:59:38.282  INFO 76061 --- [           main] com.qiha.testquartz.Main                 : Starting Main on MBYP46525326.local with PID 76061 (/Users/cheng/IdeaProjects/qiha/test-spring-boot-start/build/classes/java/main started by  cheng in /Users/ cheng/IdeaProjects/qiha)// 2023-08-26 20:59:38.350  INFO 76061 --- [           main] com.qiha.testquartz.Main                 : No active profile set, falling back to default profiles: default// 可以通过配置参数 spring.main.log-startup-info=false 设置是否输出if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}5.将启动参数applicationArguments保存到容器中6.将printedBanner保存到容器中7.设置是否允许相同beanName的bean覆盖可以通过参数spring.main.allow-bean-definition-overriding=true设置(spring-boot中默认是false)if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}8.是否开启懒加载,可以通过参数spring.main.lazy-initialization=true设置在dev环境中可以开启,加快调试速度可以在JVM参数上加-Dspring.main.lazy-initialization=true启动可以在JVM参数加参数,然后参数会被解析之后存储到environment中比如-Dtest.c=c 可以environment.getProperty("test.c")9.将启动类加入到applicationContext容器中10.发布事件ApplicationPreparedEvent,表示容器已经准备好了**/prepareContext(context, environment, listeners, applicationArguments, printedBanner);/**第八步:刷新容器1. 注册应用程序关闭的钩子函数spring.main.register-shutdown-hook=false,默认是开启的,一般开启就好了2. 刷新容器,详见3.8**/refreshContext(context);/**第九步:刷新容器后操作**/afterRefresh(context, applicationArguments);// SpringBoot启动结束,关闭秒表,并且日志打印出启动时长// 可以通过配置参数 spring.main.log-startup-info=false 设置是否输出stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {/**异常报告器的用处!!!发布事件 ApplicationFailedEvent**/handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;
}

主要流程:

1.开启计时器:

用于计算springboot项目启动的时间

2将java.awt.headless设置为true:

表示作为服务器运行,不需要显示器等输入输出设备也能运行

3.创建并开启SpringApplicationRunListener监听器

目的是在SpringBoot启动的不同阶段会发布相应的监听通知。通知贯穿应用启动的全过程。

4.将执行run方法时传入的参数封装成一个对象。

其实就是我们传入的那个args。

5.准备环境变量:

包含系统属性和用户配置的属性,比如说我们系统的环境变量。

6.打印banner信息

        这个我们可以自定义,只要在resource里面放一个banner.txt就会自动使用我们定义的banner

7. 创建 ApplicationContext 容器:

会根据 WebApplicationType 创建不同类型的容器

8.实例化异常报告器:

这个异常报告器只会捕获启动过程抛出的异常,如果是在启动完成后,在用户请求时报错,异常报告器不会捕获请求中出现的异常。

9.准备上下文环境:

        9.1--执行初始化方法:还记得第3步里面加载的初始化器嘛?其实是执行第3步加载出来的所有初始化器,实现了ApplicationContextInitializer 接口的类。就是我们上面加载的那些类

        9.2--动参数以单例的模式注册到容器中,是为了以后方便拿来使用,参数的beanName 为 :springApplicationArguments

10.刷新上下文-----这里其实就是加载bean

        ==这个过程实际上就是 Spring 容器的核心:==

涉及的关键方法:

  • invokeBeanFactoryPostProcessors

  • registerBeanPostProcessors

  • instantiateBeans

  • finishBeanFactoryInitialization

  • finishRefresh

⭕ 扫描 @Component、@Configuration,实例化 Bean,进行依赖注入等

11.刷新上下文的后置处理

12.结束计时器

13.发布上下文准备就绪事件

参考:9千字长文带你了解SpringBoot启动过程–史上最详细 SpringBoot启动流程-图文并茂-腾讯云开发者社区-腾讯云

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

相关文章:

  • Golang 的协程调度小结
  • Java-synchronized学习总结
  • 目标检测 TaskAlignedAssigner 原理
  • leetcode617.合并二叉树:递归思想下的树结构融合艺术
  • 拥塞控制算法cubic 和bbr
  • Day3 记忆内容:map set 高频操作
  • 2025年Google I/O大会上,谷歌展示了一系列旨在提升开发效率与Web体验的全新功能
  • Uniapp 串口通信原生插件开发指南(零基础版)
  • LSTM+Transformer混合模型架构文档
  • SWOT分析:MCP(Model Context Protocol)与传统编程解决方案
  • 精益数据分析(85/126):营收阶段的核心指标与盈利模型优化——从数据到商业决策的落地
  • Prompt Tuning:优化提示调优全攻略
  • 前端内容黑白处理、轮播图、奇妙的头像特效
  • Android开发namespace奇葩bug
  • 鸿蒙OSUniApp 开发实时天气查询应用 —— 鸿蒙生态下的跨端实践#三方框架 #Uniapp
  • Git 初次推送远程仓库
  • NL2SQL代表,Vanna
  • 【笔记】解决启动Anaconda Toolbox报错ModuleNotFoundError: No module named ‘pysqlite2‘
  • 从万有引力到深度学习,认识模型思维
  • ADS学习笔记(五) 谐波平衡仿真
  • 身份认证: JWT和Session是什么?
  • 深入解析 BlockingQueue:并发编程面试中的高频考点!
  • SDL2常用函数:SDL_RendererSDL_CreateRendererSDL_RenderCopySDL_RenderPresent
  • 数据库工程师备考
  • 第三届京麒CTF Web
  • ClickHouse性能优化技术深度解析与实践指南
  • (4)-Fiddler抓包-会话面板和HTTP会话数据操作
  • 多模态大语言模型arxiv论文略读(九十三)
  • Odoo 自动化规则全面深度解析
  • 探秘谷歌Gemini:开启人工智能新纪元