三分钟了解 SpringBoot 的启动流程

一、前言

背景:最近有位开发同学说面试被问到Spring Boot 的启动流程,以及被问到Spring Boot 的嵌入式Web容器是什么时候加载的。如何加载的。是怎么无缝切换的。

这些问题,其实回答起来也是比较复杂的。我们今天就从 SpringApplication.run(EasyPaasAdminApplication.class, args);入口,逐渐向下看下执行流程。来试着回答一下前面这两个问题。

后面关于SpringBoot 的web容器可以无缝随意切换为jetty,undertow…这个问题的回答涉及到Spring Boot是如何设计WebServer的。我们后续专门讲解一下。

二、执行逻辑梳理

一般我们SpringBoot 应用的启动入口都是如下这种固定的写法,
在这里插入图片描述
也可以是这样

 public static void main(String[] args) {SpringApplication application = new SpringApplication(MyApplication.class);// ... customize application settings hereapplication.run(args)}

跟过来就到这,可以看到注释运行Spring应用程序,创建并刷新一个新的ApplicationContext。
在这里插入图片描述
在这里插入图片描述
跟代码到这儿其实我们对于SpringBoot 的基本启动流程已经知道了。但是要解答什么时候启动的Tomcat 还需要继续分析。
在这里插入图片描述
到这儿我们就可以继续下去,发现Spring Boot 启动WebServer。此处的WebServer我就不展开了,可以点击去就三个方法start ,stop,getPort。可以看出来Spring 在设计接口的时候还是很严谨和精简。

我们的核心脉络是梳理SpringBoot 启动过程,并且回答Tomcat 是如何被启动的。
在这里插入图片描述
我们可以看到WebServer 的实现目前内置的有5种。其实Spring Boot 还有一个特性叫做 自动装配。

这就是为什么5个实现,我们最后启动的是Tomcat。此处也不做展开。后面我专门搞一个解析SpringBoot 自动装配的文章。
在这里插入图片描述
我们看一下内部start 的TomcatWebServer的内部实现。了解过Tomcat 源码的同学看到这儿就基本明白了。
在这里插入图片描述
好源码跟进过程我们到此结束,我们整理和总结一下。

通过扫一遍源码我们大概可以总结出来如下三个阶段:

  • 准备阶段 :Spring Boot 会加载应用程序的初始设置,并创建 Spring Boot 上下文。这个阶段的核心源码是SpringApplication 类的 run() 方法,它会调用 Spring Boot 的各个初始化器进行初始化和准备工作。
  • 应用上下文创建阶段 : Spring Boot 会创建应用程序的上下文,包括各种配置信息、Bean 的加载和初始化等。这个阶段的核心源码是Spring Boot 自动配置机制,通过扫描 classpath 中的配置文件,自动加载和配置各种组件和 Bean。
  • 刷新上下文阶段 :Spring Boot 会执行各种启动任务,包括创建 Web服务器、加载应用程序的配置、初始化各种组件等。这个阶段的核心源码是 Spring Boot的刷新机制,它会调用各种初始化器和监听器,执行各种启动任务。其中启动Tomcat 就是在这个环节进行。

三、核心源码解析

1、准备阶段

在准备阶段中,Spring Boot 会加载应用程序的初始设置,并创建 Spring Boot 上下文。这个阶段的核心源码是 SpringApplication 类的 run() 方法,它会调用 Spring Boot 的各个初始化器进行初始化和准备工作。

public ConfigurableApplicationContext run(String... args) {// 启动计时器StopWatch stopWatch = new StopWatch();stopWatch.start();// 定义应用程序上下文和异常报告器列表ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();// 配置 Headless 属性configureHeadlessProperty();// 获取 Spring Boot 启动监听器SpringApplicationRunListeners listeners = getRunListeners(args);// 执行启动监听器的 starting 方法listeners.starting();try {// 解析命令行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 准备应用程序环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);// 配置忽略 BeanInfoconfigureIgnoreBeanInfo(environment);// 打印 BannerBanner printedBanner = printBanner(environment);// 创建应用程序上下文context = createApplicationContext();// 获取异常报告器,关于异常报告,我下次专门讲一下SpringBoot 的异常收集器。exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);// 准备应用程序上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);// 刷新应用程序上下文refreshContext(context);// 刷新后操作afterRefresh(context, applicationArguments);// 停止计时器stopWatch.stop();// 记录启动日志if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 执行启动监听器的 started 方法listeners.started(context);// 执行 RunnercallRunners(context, applicationArguments);} catch (Throwable ex) {// 处理启动失败handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {// 执行启动监听器的 running 方法listeners.running(context);} catch (Throwable ex) {// 处理启动失败handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}// 返回应用程序上下文return context;}

在 run() 方法中,Spring Boot 首先会创建一个 StopWatch 对象,用于记录整个启动过程的耗时。然后,Spring Boot 会调用 getRunListeners(args) 方法获取 Spring Boot 的各个启动监听器,并调用starting() 方法通知这些监听器启动过程已经开始。接着调用 prepareEnvironment(listeners, applicationArguments) 方法创建应用程序的环境变量。

这个方法会根据用户的配置和默认设置创建一个 ConfigurableEnvironment对象,并将其传给后面的 createApplicationContext() 方法。printBanner(environment) 方法打印启动界面的 Banner,调用 refreshContext(context)方法刷新上下文。这个方法会启动上下文,执行各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会在刷新上下文阶段中进行。

2、应用上下文创建阶段

在应用上下文创建阶段中,Spring Boot 会创建应用程序的上下文,包括各种配置信息、Bean 的加载和初始化等。这个阶段的核心源码是 Spring Boot 自动配置机制,通过扫描 classpath 中的配置文件,自动加载和配置各种组件和 Bean。

protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable to create a default ApplicationContext, " +"please specify an ApplicationContextClass", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

在 createApplicationContext() 方法中,Spring Boot 首先会判断应用程序的类型,如果是 Web 应用程序,则会创建一个 WebApplicationContext;否则,会创建一个普通的 ApplicationContext。调用 BeanUtils.instantiateClass(contextClass) 方法创建应用程序的上下文。这个方法会根据上面的逻辑创建一个相应的 ApplicationContext。调用 load() 方法加载应用程序的配置。

这个方法会扫描 classpath 中的各种配置文件,例如 application.properties、application.yml、META-INF/spring.factories 等,自动配置各种组件和 Bean。调用 postProcessApplicationContext() 方法对应用程序的上下文进行后处理。这个方法会调用各种初始化器和监听器,执行各种初始化任务。

3、刷新上下文阶段

在刷新上下文阶段中,Spring Boot 会执行各种启动任务,包括创建 Web 服务器(刚才我们跟源码的时候也看到了,如上我的截图)、加载应用程序的配置、初始化各种组件等。这个阶段的核心源码是 Spring Boot 的刷新机制,它会调用各种初始化器和监听器,执行各种启动任务。

protected void refreshContext(ConfigurableApplicationContext applicationContext) {refresh(applicationContext);if (this.registerShutdownHook) {try {applicationContext.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}
}

在 refreshContext() 方法中调用 refresh(applicationContext) 方法刷新上下文。这个方法是 ApplicationContext 接口的核心方法,会启动上下文,执行各种启动任务。调用 registerShutdownHook() 方法注册应用程序的关闭钩子。这个方法会在应用程序关闭时自动执行,清理资源、关闭线程等,所以我们利用此特性在服务关闭的时候清理一些资源。并向外部发送告警通知。

在 refresh(applicationContext) 方法中,Spring Boot 会执行上下文的各种启动任务,包括创建 Web 服务器、加载应用程序的配置、初始化各种组件等。具体的启动任务会调用各种初始化器和监听器,例如:

for (ApplicationContextInitializer<?> initializer : getInitializers()) {initializer.initialize(applicationContext);
}

另外,Spring Boot 还会调用各种监听器,我们不做赘述,例如:

for (ApplicationListener<?> listener : getApplicationListeners()) {if (listener instanceof SmartApplicationListener) {SmartApplicationListener smartListener = (SmartApplicationListener) listener;if (smartListener.supportsEventType(eventType)&& smartListener.supportsSourceType(sourceType)) {invokeListener(smartListener, event);}}else if (supportsEvent(listener, eventType)) {invokeListener(listener, event);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/19225.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java】面向对象基础 之 接口

1、接口 在抽象类中&#xff0c;抽象方法本质上是定义接口规范&#xff1a;即规定高层类的接口&#xff0c;从而保证所有子类都有相同的接口实现&#xff0c;这样&#xff0c;多态就能发挥出威力。 如果一个抽象类没有字段&#xff0c;所有方法全部都是抽象方法&#xff1a; …

Python第二天之容器学习

1.List 容器无非就增删改查 1.添加 name_list [aaa,bbb,ccc,ddd] name_list.append(b1) name_list.insert(1,xxx) print(name_list)append 是在后面追加 而insert是自己定义下表插入 name_list [aaa,bbb,ccc,ddd] name_list2 [qqq,222,111] name_list.extend(name_list…

Web APls-day05

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; Window对象 BOM BOM(Browser Object Model ) 是浏览器对象模型 window对象是一个全局对象&#xff0c;也可以说是…

运维面试题

这里写目录标题 TCP介绍一下UDP TCP介绍一下 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的传输层协议。它在计算机网络中负责提供可靠的数据传输和流量控制。 TCP通过使用三次握手建立一个连接&#xff0c;确…

leaflet在天地图上添加poi兴趣点

前言 书接上节&#xff0c;在上一篇博客加载的天地图的基础上&#xff0c;加载poi兴趣点。 上节传送&#xff1a;使用leaflet在html中加载天地图且去掉左上角的缩放图标以及右下角的logo 一、加载poi的方法 leaflet通过 L.marker 方法用来加载poi&#xff0c;我们只需填入p…

python接口自动化(三十)--html测试报告通过邮件发出去——中(详解)

简介 上一篇&#xff0c;我们虽然已经将生成的最新的测试报告发出去了&#xff0c;但是MIMEText 只能发送正文&#xff0c;无法带附件&#xff0c;因此我还需要继续改造我们的代码&#xff0c;实现可以发送带有附件的邮件。发送带附件的需要导入另外一个模块 MIMEMultipart。还…

使用Yfinance和Plotly分析金融数据

大家好&#xff0c;今天我们用Python分析金融数据&#xff0c;使用Yfinance和Plotly绘制图表&#xff0c;带你了解在Python中使用Plotly制作图表&#xff0c;利用Plotly强大的图表功能来分析和可视化金融数据。 导语 在本文中&#xff0c;我们将深入研究Plotly&#xff0c;从…

Nginx系列之 一 入门

目录 一、Nginx概述 二、yum安装 三、nginx.conf配置文件详解 3.1 全局块 3.2 events 块 3.3 HTTP 块 四、Nginx 常用命令 五、Nginx代理 4.1 正向代理 4.2 反向代理 六、Nginx的Master-Worker模式 6.1 Master进程的作用是&#xff1f; 6.2 Worker进程的作用是&am…

【无标题】采用技术外包做项目开发,不得不说的四大注意事项

在进行多项目开发及多个研发供应商团队管理&#xff08;技术外包管理&#xff09;时&#xff0c;你是不是也遇到不少问题。 项目管理者A&#xff1a;在项目开发中聘用外包技术团队的过程中&#xff0c;我踩过太多的坑&#xff0c;换过一批又一批的供应商&#xff0c;之前遇到过…

【分布式应用】ELK 企业级日志分析系统

目录 一、ELK概述1.1、ELK 简介1.2 为什么要使用 ELK1.3完整日志系统基本特征1.4 ELK 的工作原理 二、ELK Elasticsearch 集群部署2.1环境准备2.2&#xff0e;部署 Elasticsearch 软件2.3安装 Elasticsearch-head 插件&#xff08;1&#xff09;编译安装 node&#xff08;2&…

三、CAS认证流程

一、相关概念 TGC(Ticket-granting cookie)&#xff1a;存放用户身份认证的cookie&#xff0c;在浏览器和CAS server用来明确用户身份的凭证&#xff1b;ST(Service Ticket)&#xff1a;CAS服务器通过浏览器分发给客户端服务器的票据&#xff0c;一个特定服务只能有一个唯一的…

机器学习与深度学习——利用随机梯度下降算法SGD对波士顿房价数据进行线性回归

机器学习与深度学习——利用随机梯度下降算法SGD对波士顿房价数据进行线性回归 我们这次使用随机梯度下降&#xff08;SGD&#xff09;算法对波士顿房价数据进行线性回归的训练&#xff0c;给出每次迭代的权重、损失和梯度&#xff0c;并且绘制损失loss随着epoch变化的曲线图。…