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

一文教会你SpringBoot是如何启动的

SpringBoot启动流程分析

流程图

未命名文件 (2).png

源码剖析

运行Application.run()方法

我们在创建好一个 SpringBoot 程序之后,肯定会包含一个类:xxxApplication,我们也是通过这个类来启动我们的程序的(梦开始的地方),而这个启动类中代码如下:
image.png
可以看到这里的代码非常的简洁,一个 main方法,在该方法中调用了 SpringApplication.run() 方法,我们也可以去看一下里面的实现。
image.png
这里的run方法接收了两个参数,一个是名为 primarySource 的类,另一个是 args 参数,其中最为主要的也就是 primarySource 参数,该参数接收了我们要启动的是哪一个类,我们把滚动条拉到最上面可以看到一个构造函数:
image.png
在这个构造函数里将我们的启动类添加到了一个 LinkedHashSet中,而在它的下面有一个 webApplicationType 参数,这就是我们用来确定应用程序类型的地方。

SpringApplication构造函数

确定应用程序类型

我们去看一下 WebApplicationType.deduceFromClasspath() 方法的实现逻辑:
image.png在这里我们确定了应用程序的容器,依照上面的代码我们可以看出来一共有三种类型:Servlet(默认)Reactive(响应式编程)None

加载所有的初始化器

我们回到 SpringApplication 类的构造器中,其中有一个 this.setInitializers()方法,用来设置我们的初始化器。
image.png
而我们的初始化器是通过扫描 META-INF/spring.factories 来知道需要加载哪些初始化器的,我们也可以去点开IDEA中的SpringBoot的jar包,我们可以看到其在 META-INF 文件夹下有一个名为 spring.factories的文件。
image.png
我们点开这个文件可以发现里面是一个又一个的全限定名,而其中有一个 ApplicationContextInitializer 的全限定名,此处就是定义初始化器的地方:
image.png
我们随便点击一个进去后发现,其实现了 ApplicationContextInitializer<ConfigurableApplicationContext>中的 initialize() 方法。
那么我们也试试能不能通过他这种写法来写一个初始化器

自定义初始化器

首先我们定义一个类来实现 ApplicationContextInitializer<ConfigurableApplicationContext>,并重写一下 initialize()方法
image.png
接着我们在 resource 目录下创建一个名为 META-INF的文件夹,并在文件夹中创建一个名为 spring.factories的文件
image.png
再在其中写上我们的初始化器的全限定名即可
image.png
接着我们启动我们的应用发现我们的打印是正常的
image.png

加载所有的监听器

同样的,我们来到SpringBoot的jar包中的spring.factories 文件中,在初始化器的下方有个 ApplicationListener,我们通过名字可以猜到,这里是定义要加载的监听器的地方
image.png
我们随便点击一个进去发现,他们和初始化器一样,都实现了一个类,监听器的类为 ApplicationListener<ContextRefreshedEvent>
image.png
我们也来试试能不能写一个自定义的监听器给加载上。

自定义监听器

首先定义一个类来实现ApplicationListener 中的 onApplicationEvent()方法
image.png
再在spring.factories中来定义一下我们要加载的监听器
image.png
接着我们启动一下项目,可以看到我们的监听器成功被加载了,并且也在初始化器的后面
image.png

设置程序运行的主类

我们重新回到 SpringApplication中的构造器中,而其中的最后一行就是去设置我们程序运行的主类
image.png
而我们点入方法看一眼
image.png
我们看代码可以看到,他在寻找方法名为 main的类,并且将其返回出去,也就是说我们的程序是通过这个方法来推断我们程序的主类在哪里的。
至此,我们构造函数就执行完毕了,接下来就会进入到run方法中来运行我们的程序。

run() 方法

开启计时器

其实在原先的版本中是开启计时器,但是在新版本中使用的是通过 System.nanoTime()互减的方法来实现计时的,如下:
image.png
其最主要的作用是来计算程序启动过程中使用的时长

启用Headless模式

在run方法中我们可以看到执行了 this.configureHeadlessProperty()的方法
image.png
我们来到这个方法体中,可以看到这里是用来获取 java.awt.headless
image.png
其目的是为了让程序可以在没有显示器和鼠标的情况下也可以正常工作,用来模拟输入和输出设备

获取并开启监听器

在我们开启了 Headless 模式 后,程序获取了监听器,并将其开启了,程序如下:
image.png
那么其是如何获取监听器的呢?
image.png点进来后我们可以发现,他是通过加载 spring.factories的配置来获取到所有的监听器的,也就是刚才我们说的地方

设置应用程序参数

image.png
在这里程序是使用了默认的参数配置,如下:
image.png
此处的 args 就是我们的程序入口传入的args
image.png

准备环境变量

当我们的应用程序参数设置完成后,程序会开始准备环境变量
image.png
我们进入到 this.prepareEnvironment() 的方法体中,并在最后返回的地方打个断点
image.png
可以看到我们的环境变量均被加载进来了

忽略Bean信息

这里是将 spring.beaninfo.ignore的值设置为true,没什么好说的,原理和上面 启用Headless模式 一样
image.png

image.png

打印Banner信息

image.png
我们在这个地方来打印程序的banner,也就是我们程序运行时打印的logo
image.png
它是有一个默认值的,定义在SpringBootBanner
image.png
我们想要更改时只需要在 resource 下创建一个名为 banner.txt 的文件即可
image.png
最后我们启动就可以得到如下的输出
image.png

创建程序上下文

程序通过执行 createApplicationContext() 方法来进行创建程序的上下文对象
image.png
此处就是利用反射来创建对象

实例化异常报告器

当我们启动出错时会被捕获异常,并且执行一个名为 handleRunFailure() 的方法
image.png
我们点进去可以看到其中有一个 getExceptionReporters() 的方法
image.png
image.png
我们可以清晰的看到上面调用了getSpringFactoriesInstances()方法,此处就是在我们的 spring.factories中获取参数的方法,上面我们也提到过很多次,这里就不过多赘述了。
image.png
我们点进去发现其也是实现了一个类并重写其中的方法
image.png
那么我们也去定义一个自己的异常报告器来试试

自定义异常报告器

首先我们要先创建一个类来实现 SpringBootExceptionReporter 中的 onApplicationEvent()方法
image.png
然后在 spring.factories中定义即可
image.png
但是需要注意的是,我们的程序在执行不出错的情况下,异常报告器是不会执行的,所以我们要手动制造一个错误来使其报错。那么我们就在加载我们自定义的监听器时主动抛出一个异常。
image.png
接着我们运行程序就会得到以下结果:
image.png

准备上下文

此处程序执行了一个名为 prepareContext() 的方法
image.png
我们到方法体内可以得到如下代码:
image.png
其中比较重要的就是 postProcessApplicationContext()applyInitializers()beanFactory.registerSingleton("springApplicationArguments", applicationArguments)这三个方法,接下来我们逐一去分析

postProcessApplicationContext()

方法体如下:
image.png
其中最主要的就是这个 beanNameGenerator ,也就是 Bean名称生成器,主要的作用就是用来 创建Bean对象的名称

applyInitializers()

image.png
通过以上方法体,我们可以看到,其中有一个迭代器用于遍历,并且均执行了 initializer.initialize(context 方法,此处也就是我们的初始化方法。
那么该方法的作用就是来执行所有的初始化方法,而我们程序中的初始化器在 SpringBoot构造函数 阶段就已经加载完毕了,其实实质就是来执行我们所有的初始化器中的初始化方法。

beanFactory.registerSingleton()

image.png
此处的代码我们就更好理解了,其创建了一个 Bean工厂,并且以单例模式注册了一个东西,那么是什么呢?
没错,就是名为springApplicationArguments 的参数,但是我们英语水平不够,不知道它是什么意思怎么办?
没关系,科技使人进步,我们还有翻译软件ヾ(≧▽≦*)o
image.png
没错,是应用程序参数!我们将启动的参数以单例模式注册到我们的容器中,其目的是为了方便之后的读取使用。

刷新上下文

image.png
这里就是单纯的刷新上下文,我们之前学习的自动装配和Tomcat的启动就是在这里完成的

刷新上下文的后置处理

image.png
这里是启动后的一些处理,暂时这个方法是空的,留给用户自定义。
既然如此,那为什么不来点自定义的 afterRefresh()尝尝鲜呢o( ̄▽ ̄)ブ
首先我们要自定义一个类来继承 SpringApplication 并重写其中的 afterRefresh 方法
image.png
接着我们要去改造我们的程序入口
image.png
最后我们启动我们的程序就可以得到:
image.png

结束计时器

在老版本中我们是使用 stopWatch来完成计时器的功能的,前面也讲了,在新版本中我们是使用时间戳互减来完成我们计时的功能的
image.png

发布上下文准备就绪事件

image.png
其目的就是告诉应用程序:嘿哥们儿,我准备好了,咱们可以开始工作了。

执行自定义的run()方法

image.png
在此处我们可以看到,其加载了两个类型的run方法,一种是 ApplicationRunner,另一种是 CommandLineRunner,该方法将这两种类型的所有runnner都添加到一个 ArrayList 中,并进行排序。
在排序完成后就由迭代器来逐一执行runner的 callRunner() 方法。
那么我们也可以自定义我们的runner来使其执行。
来吧老伙计,都最后一个步骤了,跟着我一起实现一下。
image.png
只需要自定义类并实现 ApplicationRunnerCommandLineRunner并重写其中的 run()方法即可,此处为了同时演示两种方法,我就同时实现了两个类型,大家可以根据实际情况来选择。
最后我们运行程序就可以得到:
image.png
至此,我们的SpringBoot就运行完成了。
感谢观看。

相关文章:

一文教会你SpringBoot是如何启动的

SpringBoot启动流程分析 流程图 源码剖析 运行Application.run()方法 我们在创建好一个 SpringBoot 程序之后&#xff0c;肯定会包含一个类&#xff1a;xxxApplication&#xff0c;我们也是通过这个类来启动我们的程序的&#xff08;梦开始的地方&#xff09;&#xff0c;而…...

车载测试面试:各大车企面试题汇总

本博主可协助大家成功进军车载测试行业 TBOX 深圳 涉及过T-BOX测试吗Ota升级涉及的台架环境是什么样的&#xff1f;上车实测之前有没有一个仿真环境台架环境都什么零部件T-BOX了解多少Linux和shell有接触吗 单片机uds诊断是在实车上座的吗 uds在实车上插的那口 诊断仪器是哪…...

Qt散文一

Qt的事件分为普通事件和系统事件&#xff0c;普通事件比如用户按下键盘&#xff0c;系统事件比如定时器事件。事件循环的开始是从main函数的QApplication&#xff0c;然后调用exec()开始的&#xff0c;在执行exec()函数之后&#xff0c;程序将进入事件循环来监听应用程序的事件…...

MySQL学习Day32——数据库备份与恢复

在任何数据库环境中&#xff0c;总会有不确定的意外情况发生&#xff0c;比如例外的停电、计算机系统中的各种软硬件故障、人为破坏、管理员误操作等是不可避免的&#xff0c;这些情况可能会导致数据的丢失、 服务器瘫痪等严重的后果。存在多个服务器时&#xff0c;会出现主从服…...

阅读基础知识

一 网络 1. 三次握手四次挥手 三次握手&#xff1a;为了建立长链接进行交互即建立一个会话&#xff0c;使用 http/https 协议 ① 客户端产生初始化序列号 Seqx &#xff0c;向服务端发送建立连接的请求报文&#xff0c;将 SYN1 同步序列号&#xff1b; ② 服务端接收建立连接…...

【NestJS 编程艺术】1. NestJS设计模式深度解析:构建高效、可维护的服务端应用

在当今快速发展的软件开发领域&#xff0c;Node.js凭借其轻量级和高性能的特点&#xff0c;已经成为了构建服务端应用的首选技术之一。然而&#xff0c;随着应用规模的扩大&#xff0c;传统的Node.js框架如Express和Koa可能在架构设计和代码组织上显得力不从心。这时&#xff0…...

QT中connect()的参数5:Qt::DirectConnection、Qt::QueuedConnection区别

原文链接&#xff1a;https://blog.csdn.net/Dasis/article/details/120916993 connect用于连接QT的信号和槽&#xff0c;在qt编程过程中不可或缺。它其实有第5个参数&#xff0c;只是一般使用默认值&#xff0c;在满足某些特殊需求的时候可能需要手动设置。 Qt::AutoConnect…...

VXLAN学习笔记

声明&#xff1a;该博客内容大部分参考参考链接整理 什么是VXLAN&#xff1f; VXLAN(Virtual Extensible LAN)即虚拟扩展局域网&#xff0c;是大二层网络中广泛使用的网络虚拟化技术。在源网络设备与目的网络设备之间建立一条逻辑VXLAN隧道&#xff0c;采用MAC in UDP的封装方…...

全排列的不同写法(茴字的不同写法)及对应的时间开销

资源课件&#xff1a; CS106B-recursion-pptstanford library-timer.hstanford library-set.h 不同的方法 1------ Set<string> permutations1Rec(string remaining) {Set<string> res;if(remaining.size() 0) {res "";}else {for(int i 0; i <…...

权衡后台数据库设计中是否使用外键

目录 引言 外键简介 对比 真实后台项目中的权衡 结论 引言 在大学学习数据库课程时&#xff0c;我们会早早的接触到外键这一概念&#xff0c;同时我相信大部分人在懂了外键的概念后都会觉得外键很重要&#xff0c;在涉及多表一定要用&#xff0c;但后来在我接触到真实项目…...

ChatGPT提示词方法的原理

关于提示词&#xff0c;我之前的一些文章可以参考&#xff1a; 【AIGC】AI作图最全提示词prompt集合&#xff08;收藏级&#xff09;https://giszz.blog.csdn.net/article/details/134815245?ydrefereraHR0cHM6Ly9tcC5jc2RuLm5ldC9tcF9ibG9nL21hbmFnZS9hcnRpY2xlP3NwbT0xMDExL…...

计算机网络 谢希仁(001-1)

计算机网络-方老师 总时长 24:45:00 共50个视频&#xff0c;6个模块 此文章包含1.1到1.4的内容 简介 1.1计算机网络的作用 三网融合&#xff08;三网合一&#xff09; 模拟信号就是连续信号 数字信号是离散信号 1.2互联网概述 以前2兆带宽就要98 现在几百兆带宽也就几百块 …...

Windows,MacOS,Linux下载python并配置环境图文讲解

Windows 打开python官网 点击download 点击黄色按钮 另存为 打开文件 全选 配置安装路径 安装中 关闭路径长度限制 完成 验证 同时按住winr(win就是空格键左边的东西) 输入cmd 键入python,如果出现版本(红框)即安装成功 MacOS 同理打开python官网 点击最新版本 拖…...

汽车网络基础知识 要点

在以太网开发中&#xff0c;常常会听到一些专业名词&#xff0c;例如PHY&#xff0c;MAC&#xff0c;MII&#xff0c;switch&#xff0c;下面是解释 PHY PHY 是物理接口收发器&#xff0c;它实现物理层。包括 MII/GMII (介质独立接口) 子层、PCS (物理编码子层) 、PMA (物理介…...

ClickHouse中的设置的分类

ClickHouse中的各种设置 ClickHouse中的设置有几百个&#xff0c;下面对这些设置做了一个简单的分类。...

香港空间服务器带宽和流量限制:原因和解决方法

​  香港空间服务器&#xff0c;也被称作香港虚拟服务器。一般情况下&#xff0c;香港空间服务器所提供的流量或者带宽&#xff0c;是足以满足99%的普通中小网站用户使用的&#xff0c;但也不排除&#xff0c;网站访问量大&#xff0c;租香港空间不能够满足要求的情况。 在本…...

echarts实践总结(常用一):柱状图(特点:渐变色、点击缩放、左右滑动、悬浮展示样式)

目录 第一章 echarts基本使用 第二章 echarts实践——柱状图 效果展示 第一章 echarts基本使用 Echarts常用配置项(详细入门)_echarts配置项手册-CSDN博客 第二章 echarts实践——柱状图 最近接到这么一个需求&#xff0c;需要画页面&#xff0c;然后有这么几个echarts的图需…...

CVE-2020-6418:Incorrect side effect modelling for JSCreate

文章目录 环境搭建漏洞分析漏洞利用漏洞触发链RCE 总结参考 环境搭建 sudo apt install python git reset --hard cecaa443ec29784ee26e31e678a333a3c1e71136 gclient sync -D// 手动引入漏洞&#xff0c;参考下面的 patch&#xff0c;把相关修改注释掉即可// debug version t…...

STM32信息安全 1.2 课程架构介绍:芯片生命周期管理与安全调试

STM32信息安全 1.2 课程架构介绍&#xff1a;STM32H5 芯片生命周期管理与安全调试 下面开始学习课程的第二节&#xff0c;简单介绍下STM32H5芯片的生命周期和安全调试&#xff0c;具体课程大家可以观看STM32官方录制的课程&#xff0c;链接&#xff1a;1.2. 课程架构介绍&…...

springboot278基于JavaWeb的鲜牛奶订购系统的设计与实现

鲜牛奶订购系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统鲜牛奶订购信息管理难度大&…...

GESP6级C++考试语法知识(二十六、广度优先搜索(一、认识BFS))

第一课《消息传播城——认识广度优先搜索 BFS》&#x1f31f;一、故事开始&#xff1a;国王的紧急消息1、很久很久以前&#xff0c;有一座叫&#xff1a;&#x1f3f0;「消息传播城」的大王国。2、有一天&#xff0c;怪兽突然来袭&#xff01;国王必须立刻通知所有村庄&#xf…...

深入浅出arm7架构下大模型API调用,Taotoken多模型聚合平台接入指南

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 深入浅出arm7架构下大模型API调用&#xff0c;Taotoken多模型聚合平台接入指南 对于在arm7架构设备上进行开发的工程师而言&#x…...

Linux系统服务“窃听”与“喊话”:dbus-monitor/dbus-send实战指南(以systemd-logind为例)

Linux系统服务的“窃听”与“喊话”&#xff1a;dbus-monitor/dbus-send高阶实战指南当你坐在咖啡馆里&#xff0c;周围此起彼伏的对话声中&#xff0c;偶尔会捕捉到一些有趣的片段——这正是dbus-monitor在Linux系统中的角色。而当你需要主动与某人交流时&#xff0c;清晰明确…...

Mermaid在线编辑器:3步掌握技术文档图表制作的终极指南

Mermaid在线编辑器&#xff1a;3步掌握技术文档图表制作的终极指南 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-live-edito…...

Unity中Newtonsoft.Json三种安装方式深度对比

1. 为什么Unity项目里装个Json库要纠结三天&#xff1f;——从一次崩溃说起Newtonsoft.Json&#xff0c;也就是大家常说的Json.NET&#xff0c;在C#生态里几乎是序列化的代名词。但放到Unity里&#xff0c;它却是个“熟悉的陌生人”&#xff1a;你写惯了JsonConvert.SerializeO…...

量子纠缠分发技术在城域网络中的实践与优化

1. 量子纠缠分发技术概述量子纠缠是量子力学中最奇特的现象之一&#xff0c;两个或多个量子系统之间可以形成一种强关联&#xff0c;这种关联无法用经典物理理论解释。在量子通信领域&#xff0c;纠缠光子对的分发是实现量子密钥分发、量子隐形传态等应用的基础。传统实验室环境…...

8051指令集手册获取与开发优化指南

1. 8051指令集手册获取指南作为一名从事嵌入式开发十余年的工程师&#xff0c;我深知指令集手册在单片机开发中的核心地位。对于8051架构开发者而言&#xff0c;准确理解每条指令的机器周期、标志位影响和寻址方式是写出高效代码的基础。本文将系统梳理获取权威8051指令集资源的…...

用CUDA C++手搓LeNet推理引擎:从PyTorch导出权重到GPU加速的完整避坑指南

用CUDA C手搓LeNet推理引擎&#xff1a;从PyTorch导出权重到GPU加速的完整避坑指南在深度学习模型部署的最后一公里&#xff0c;将训练好的模型高效移植到生产环境是每个开发者必须面对的挑战。本文将带您深入实践&#xff0c;从PyTorch训练好的LeNet模型出发&#xff0c;完整实…...

基于SVD/HOSVD与DLinear的流体场高分辨率预测模型解析

1. 项目概述&#xff1a;当流体动力学遇上智能预测在计算流体动力学&#xff08;CFD&#xff09;和科学机器学习&#xff08;SciML&#xff09;的交叉领域&#xff0c;我们每天都在和数据洪流搏斗。一次高保真度的湍流模拟&#xff0c;动辄产生TB级的高维时空数据——速度场、压…...

在线机器学习在时序异常检测中的应用:OML-AD原理与工程实践

1. 项目概述&#xff1a;当异常检测遇上实时数据流在运维监控、金融风控或物联网传感器分析中&#xff0c;我们常常需要盯着一条条不断涌出的时间序列数据&#xff0c;从中揪出那些“不对劲”的点——也就是异常。传统的玩法&#xff0c;比如训练一个SARIMA或者Prophet模型&…...