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

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启动流程-图文并茂-腾讯云开发者社区-腾讯云

相关文章:

  • 实验四:用户管理和sudo提权
  • 【Redis】3-Redis应用
  • 大语言模型(LLM)入门项目推荐
  • 【springMVC】springMVC学习系列一:springMVC的组件
  • 经典分类模型
  • C#编解码:Base64扩展类的实现与应用
  • 一、奋斗类(事业奋斗/梦想实现)
  • ACM Latex模板:合并添加作者和单位
  • 智能护航 安心畅游——AI智能监控系统解决方案
  • 双11、618大促要做什么?
  • 报错:ImportError: cannot import name ‘metadata‘ from ‘importlib‘
  • IAM角色访问AWS RDS For MySQL
  • Linux云计算训练营笔记day16(Linux周期性计划任务、Python)
  • Prometheus、Exporter 和 Grafana:性能分析铁三角
  • 两阶段法目标检测发展脉络
  • Python 实现简单车牌识别
  • 【01】大模型原理与API使用
  • 第2章(新)Day2 - Python基础入门
  • 设计模式-简单工厂模式
  • #git 储藏库意外被清空 Error: bad index – Fatal: index file corrupt
  • 微信公众号网站导航怎么做/域名解析ip地址
  • 室内设计8年熬不起了/百度seo优化技术
  • 岳阳汨罗网站建设/磁力狗在线
  • wordpress 注册菜单/seo经验
  • 专业做网站推广的公司/中文搜索引擎排行榜
  • 网络兼职/如何提高seo关键词排名