一文教会你SpringBoot是如何启动的
SpringBoot启动流程分析
流程图
源码剖析
运行Application.run()方法
我们在创建好一个 SpringBoot
程序之后,肯定会包含一个类:xxxApplication
,我们也是通过这个类来启动我们的程序的(梦开始的地方),而这个启动类中代码如下:
可以看到这里的代码非常的简洁,一个 main
方法,在该方法中调用了 SpringApplication.run()
方法,我们也可以去看一下里面的实现。
这里的run方法接收了两个参数,一个是名为 primarySource
的类,另一个是 args
参数,其中最为主要的也就是 primarySource
参数,该参数接收了我们要启动的是哪一个类,我们把滚动条拉到最上面可以看到一个构造函数:
在这个构造函数里将我们的启动类添加到了一个 LinkedHashSet
中,而在它的下面有一个 webApplicationType
参数,这就是我们用来确定应用程序类型的地方。
SpringApplication构造函数
确定应用程序类型
我们去看一下 WebApplicationType.deduceFromClasspath()
方法的实现逻辑:
在这里我们确定了应用程序的容器,依照上面的代码我们可以看出来一共有三种类型:Servlet(默认)
、Reactive(响应式编程)
和 None
。
加载所有的初始化器
我们回到 SpringApplication
类的构造器中,其中有一个 this.setInitializers()
方法,用来设置我们的初始化器。
而我们的初始化器是通过扫描 META-INF/spring.factories 来知道需要加载哪些初始化器的,我们也可以去点开IDEA中的SpringBoot的jar包,我们可以看到其在 META-INF 文件夹下有一个名为 spring.factories
的文件。
我们点开这个文件可以发现里面是一个又一个的全限定名,而其中有一个 ApplicationContextInitializer
的全限定名,此处就是定义初始化器的地方:
我们随便点击一个进去后发现,其实现了 ApplicationContextInitializer<ConfigurableApplicationContext>
中的 initialize()
方法。
那么我们也试试能不能通过他这种写法来写一个初始化器
自定义初始化器
首先我们定义一个类来实现 ApplicationContextInitializer<ConfigurableApplicationContext>
,并重写一下 initialize()
方法
接着我们在 resource 目录下创建一个名为 META-INF
的文件夹,并在文件夹中创建一个名为 spring.factories
的文件
再在其中写上我们的初始化器的全限定名即可
接着我们启动我们的应用发现我们的打印是正常的
加载所有的监听器
同样的,我们来到SpringBoot的jar包中的spring.factories
文件中,在初始化器的下方有个 ApplicationListener
,我们通过名字可以猜到,这里是定义要加载的监听器的地方
我们随便点击一个进去发现,他们和初始化器一样,都实现了一个类,监听器的类为 ApplicationListener<ContextRefreshedEvent>
我们也来试试能不能写一个自定义的监听器给加载上。
自定义监听器
首先定义一个类来实现ApplicationListener 中的 onApplicationEvent()
方法
再在spring.factories
中来定义一下我们要加载的监听器
接着我们启动一下项目,可以看到我们的监听器成功被加载了,并且也在初始化器的后面
设置程序运行的主类
我们重新回到 SpringApplication
中的构造器中,而其中的最后一行就是去设置我们程序运行的主类
而我们点入方法看一眼
我们看代码可以看到,他在寻找方法名为 main
的类,并且将其返回出去,也就是说我们的程序是通过这个方法来推断我们程序的主类在哪里的。
至此,我们构造函数就执行完毕了,接下来就会进入到run方法中来运行我们的程序。
run() 方法
开启计时器
其实在原先的版本中是开启计时器,但是在新版本中使用的是通过 System.nanoTime()
互减的方法来实现计时的,如下:
其最主要的作用是来计算程序启动过程中使用的时长
启用Headless模式
在run方法中我们可以看到执行了 this.configureHeadlessProperty()
的方法
我们来到这个方法体中,可以看到这里是用来获取 java.awt.headless
值
其目的是为了让程序可以在没有显示器和鼠标的情况下也可以正常工作,用来模拟输入和输出设备
获取并开启监听器
在我们开启了 Headless 模式
后,程序获取了监听器,并将其开启了,程序如下:
那么其是如何获取监听器的呢?
点进来后我们可以发现,他是通过加载 spring.factories
的配置来获取到所有的监听器的,也就是刚才我们说的地方
设置应用程序参数
在这里程序是使用了默认的参数配置,如下:
此处的 args 就是我们的程序入口传入的args
准备环境变量
当我们的应用程序参数设置完成后,程序会开始准备环境变量
我们进入到 this.prepareEnvironment()
的方法体中,并在最后返回的地方打个断点
可以看到我们的环境变量均被加载进来了
忽略Bean信息
这里是将 spring.beaninfo.ignore
的值设置为true,没什么好说的,原理和上面 启用Headless模式 一样
打印Banner信息
我们在这个地方来打印程序的banner,也就是我们程序运行时打印的logo
它是有一个默认值的,定义在SpringBootBanner
中
我们想要更改时只需要在 resource
下创建一个名为 banner.txt
的文件即可
最后我们启动就可以得到如下的输出
创建程序上下文
程序通过执行 createApplicationContext()
方法来进行创建程序的上下文对象
此处就是利用反射来创建对象
实例化异常报告器
当我们启动出错时会被捕获异常,并且执行一个名为 handleRunFailure()
的方法
我们点进去可以看到其中有一个 getExceptionReporters()
的方法
我们可以清晰的看到上面调用了getSpringFactoriesInstances()
方法,此处就是在我们的 spring.factories
中获取参数的方法,上面我们也提到过很多次,这里就不过多赘述了。
我们点进去发现其也是实现了一个类并重写其中的方法
那么我们也去定义一个自己的异常报告器来试试
自定义异常报告器
首先我们要先创建一个类来实现 SpringBootExceptionReporter
中的 onApplicationEvent()
方法
然后在 spring.factories
中定义即可
但是需要注意的是,我们的程序在执行不出错的情况下,异常报告器是不会执行的,所以我们要手动制造一个错误来使其报错。那么我们就在加载我们自定义的监听器时主动抛出一个异常。
接着我们运行程序就会得到以下结果:
准备上下文
此处程序执行了一个名为 prepareContext()
的方法
我们到方法体内可以得到如下代码:
其中比较重要的就是 postProcessApplicationContext()
、applyInitializers()
和 beanFactory.registerSingleton("springApplicationArguments", applicationArguments)
这三个方法,接下来我们逐一去分析
postProcessApplicationContext()
方法体如下:
其中最主要的就是这个 beanNameGenerator ,也就是 Bean名称生成器,主要的作用就是用来 创建Bean对象的名称
applyInitializers()
通过以上方法体,我们可以看到,其中有一个迭代器用于遍历,并且均执行了 initializer.initialize(context
方法,此处也就是我们的初始化方法。
那么该方法的作用就是来执行所有的初始化方法,而我们程序中的初始化器在 SpringBoot构造函数 阶段就已经加载完毕了,其实实质就是来执行我们所有的初始化器中的初始化方法。
beanFactory.registerSingleton()
此处的代码我们就更好理解了,其创建了一个 Bean工厂,并且以单例模式注册了一个东西,那么是什么呢?
没错,就是名为springApplicationArguments
的参数,但是我们英语水平不够,不知道它是什么意思怎么办?
没关系,科技使人进步,我们还有翻译软件ヾ(≧▽≦*)o
没错,是应用程序参数!我们将启动的参数以单例模式注册到我们的容器中,其目的是为了方便之后的读取使用。
刷新上下文
这里就是单纯的刷新上下文,我们之前学习的自动装配和Tomcat的启动就是在这里完成的
刷新上下文的后置处理
这里是启动后的一些处理,暂时这个方法是空的,留给用户自定义。
既然如此,那为什么不来点自定义的 afterRefresh()
尝尝鲜呢o( ̄▽ ̄)ブ
首先我们要自定义一个类来继承 SpringApplication
并重写其中的 afterRefresh
方法
接着我们要去改造我们的程序入口
最后我们启动我们的程序就可以得到:
结束计时器
在老版本中我们是使用 stopWatch
来完成计时器的功能的,前面也讲了,在新版本中我们是使用时间戳互减来完成我们计时的功能的
发布上下文准备就绪事件
其目的就是告诉应用程序:嘿哥们儿,我准备好了,咱们可以开始工作了。
执行自定义的run()方法
在此处我们可以看到,其加载了两个类型的run方法,一种是 ApplicationRunner
,另一种是 CommandLineRunner
,该方法将这两种类型的所有runnner都添加到一个 ArrayList 中,并进行排序。
在排序完成后就由迭代器来逐一执行runner的 callRunner()
方法。
那么我们也可以自定义我们的runner来使其执行。
来吧老伙计,都最后一个步骤了,跟着我一起实现一下。
只需要自定义类并实现 ApplicationRunner
或 CommandLineRunner
并重写其中的 run()
方法即可,此处为了同时演示两种方法,我就同时实现了两个类型,大家可以根据实际情况来选择。
最后我们运行程序就可以得到:
至此,我们的SpringBoot就运行完成了。
感谢观看。
相关文章:
一文教会你SpringBoot是如何启动的
SpringBoot启动流程分析 流程图 源码剖析 运行Application.run()方法 我们在创建好一个 SpringBoot 程序之后,肯定会包含一个类:xxxApplication,我们也是通过这个类来启动我们的程序的(梦开始的地方),而…...
车载测试面试:各大车企面试题汇总
本博主可协助大家成功进军车载测试行业 TBOX 深圳 涉及过T-BOX测试吗Ota升级涉及的台架环境是什么样的?上车实测之前有没有一个仿真环境台架环境都什么零部件T-BOX了解多少Linux和shell有接触吗 单片机uds诊断是在实车上座的吗 uds在实车上插的那口 诊断仪器是哪…...
Qt散文一
Qt的事件分为普通事件和系统事件,普通事件比如用户按下键盘,系统事件比如定时器事件。事件循环的开始是从main函数的QApplication,然后调用exec()开始的,在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件…...
MySQL学习Day32——数据库备份与恢复
在任何数据库环境中,总会有不确定的意外情况发生,比如例外的停电、计算机系统中的各种软硬件故障、人为破坏、管理员误操作等是不可避免的,这些情况可能会导致数据的丢失、 服务器瘫痪等严重的后果。存在多个服务器时,会出现主从服…...
阅读基础知识
一 网络 1. 三次握手四次挥手 三次握手:为了建立长链接进行交互即建立一个会话,使用 http/https 协议 ① 客户端产生初始化序列号 Seqx ,向服务端发送建立连接的请求报文,将 SYN1 同步序列号; ② 服务端接收建立连接…...
【NestJS 编程艺术】1. NestJS设计模式深度解析:构建高效、可维护的服务端应用
在当今快速发展的软件开发领域,Node.js凭借其轻量级和高性能的特点,已经成为了构建服务端应用的首选技术之一。然而,随着应用规模的扩大,传统的Node.js框架如Express和Koa可能在架构设计和代码组织上显得力不从心。这时࿰…...
QT中connect()的参数5:Qt::DirectConnection、Qt::QueuedConnection区别
原文链接:https://blog.csdn.net/Dasis/article/details/120916993 connect用于连接QT的信号和槽,在qt编程过程中不可或缺。它其实有第5个参数,只是一般使用默认值,在满足某些特殊需求的时候可能需要手动设置。 Qt::AutoConnect…...
VXLAN学习笔记
声明:该博客内容大部分参考参考链接整理 什么是VXLAN? VXLAN(Virtual Extensible LAN)即虚拟扩展局域网,是大二层网络中广泛使用的网络虚拟化技术。在源网络设备与目的网络设备之间建立一条逻辑VXLAN隧道,采用MAC in UDP的封装方…...
全排列的不同写法(茴字的不同写法)及对应的时间开销
资源课件: 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 <…...
权衡后台数据库设计中是否使用外键
目录 引言 外键简介 对比 真实后台项目中的权衡 结论 引言 在大学学习数据库课程时,我们会早早的接触到外键这一概念,同时我相信大部分人在懂了外键的概念后都会觉得外键很重要,在涉及多表一定要用,但后来在我接触到真实项目…...
ChatGPT提示词方法的原理
关于提示词,我之前的一些文章可以参考: 【AIGC】AI作图最全提示词prompt集合(收藏级)https://giszz.blog.csdn.net/article/details/134815245?ydrefereraHR0cHM6Ly9tcC5jc2RuLm5ldC9tcF9ibG9nL21hbmFnZS9hcnRpY2xlP3NwbT0xMDExL…...
计算机网络 谢希仁(001-1)
计算机网络-方老师 总时长 24:45:00 共50个视频,6个模块 此文章包含1.1到1.4的内容 简介 1.1计算机网络的作用 三网融合(三网合一) 模拟信号就是连续信号 数字信号是离散信号 1.2互联网概述 以前2兆带宽就要98 现在几百兆带宽也就几百块 …...
Windows,MacOS,Linux下载python并配置环境图文讲解
Windows 打开python官网 点击download 点击黄色按钮 另存为 打开文件 全选 配置安装路径 安装中 关闭路径长度限制 完成 验证 同时按住winr(win就是空格键左边的东西) 输入cmd 键入python,如果出现版本(红框)即安装成功 MacOS 同理打开python官网 点击最新版本 拖…...
汽车网络基础知识 要点
在以太网开发中,常常会听到一些专业名词,例如PHY,MAC,MII,switch,下面是解释 PHY PHY 是物理接口收发器,它实现物理层。包括 MII/GMII (介质独立接口) 子层、PCS (物理编码子层) 、PMA (物理介…...
ClickHouse中的设置的分类
ClickHouse中的各种设置 ClickHouse中的设置有几百个,下面对这些设置做了一个简单的分类。...
香港空间服务器带宽和流量限制:原因和解决方法
香港空间服务器,也被称作香港虚拟服务器。一般情况下,香港空间服务器所提供的流量或者带宽,是足以满足99%的普通中小网站用户使用的,但也不排除,网站访问量大,租香港空间不能够满足要求的情况。 在本…...
echarts实践总结(常用一):柱状图(特点:渐变色、点击缩放、左右滑动、悬浮展示样式)
目录 第一章 echarts基本使用 第二章 echarts实践——柱状图 效果展示 第一章 echarts基本使用 Echarts常用配置项(详细入门)_echarts配置项手册-CSDN博客 第二章 echarts实践——柱状图 最近接到这么一个需求,需要画页面,然后有这么几个echarts的图需…...
CVE-2020-6418:Incorrect side effect modelling for JSCreate
文章目录 环境搭建漏洞分析漏洞利用漏洞触发链RCE 总结参考 环境搭建 sudo apt install python git reset --hard cecaa443ec29784ee26e31e678a333a3c1e71136 gclient sync -D// 手动引入漏洞,参考下面的 patch,把相关修改注释掉即可// debug version t…...
STM32信息安全 1.2 课程架构介绍:芯片生命周期管理与安全调试
STM32信息安全 1.2 课程架构介绍:STM32H5 芯片生命周期管理与安全调试 下面开始学习课程的第二节,简单介绍下STM32H5芯片的生命周期和安全调试,具体课程大家可以观看STM32官方录制的课程,链接:1.2. 课程架构介绍&…...
springboot278基于JavaWeb的鲜牛奶订购系统的设计与实现
鲜牛奶订购系统的设计与实现 摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统鲜牛奶订购信息管理难度大&…...
SSH介绍及检测规则思路分析
一、SSH 1、定义 SSH是安全的加密协议,用于远程连接linux服务器。 2、ssh服务的主要功能: 1)提供远程链接服务器的功能; 2)对远程链接传输的数据进行加密 3、ssh与telnet的区别: 服务链接方式 服务数据…...
React核心⼊⻔-lesson1
自学React从入门到精通,从使用到写源码 React⼊⻔ 课堂⽬标资源起步 ⽂件结构⽂件结构⼀览React和ReactDomJSX 使⽤JSX组件 组件的两种形式 class组件function组件组件状态管理 类组件中的状态管理函数组件中的状态管理事件处理组件通信 Props属性传递contextredux⽣命周期 变…...
数据结构(三)——栈
三、栈、队列和数组 3.1 栈 3.1.1 栈的基本概念 线性表是具有相同数据类型的n(n≥0)个数据元素的有限 序列,其中n为表长,当n 0时线 性表是一个空表。若用L命名线性表,则其一般表示为 L (a1, a2, … , ai , ai1, ……...
【Redis知识点总结】(五)——Redis实现分布式锁
Redis知识点总结(五)——Redis实现分布式锁 setnxsetnx expiresetnx expire lua脚本set nx exset nx ex 随机值set nx ex 随机值 lua脚本set ex nx 随机值 lua脚本 锁续期RedissonRedLock 在Redis的众多应用场景中,分布式锁是Redis比…...
CSS 绝对定位 position:absolute
什么是CSS绝对定位absolute定位? 绝对定位absolute定位是CSS中的一种定位方式,可以将元素精确定位到一个确定的点,这与元素在文档流上的自然位置无关。相比起其他定位方式,绝对定位很灵活性,它可以将元素脱离文档流&am…...
鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:RelativeContainer)
相对布局组件,用于复杂场景中元素对齐的布局。 说明: 该组件从API Version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 规则说明 容器内子组件区分水平方向,垂直方向: 水平方向为left&…...
Android制作微信添加多个图片,放大图片
1.添加依赖 implementation com.github.bumptech.glide:glide:4.12.0 //裁剪图片等等 implementation androidx.recyclerview:recyclerview:1.1.0 //recycleview依赖 2.使用recycleview <androidx.recyclerview.widget.RecyclerViewandroid:id"id/recyclerView"…...
iOS runtime理解和应用场景
一、runtime的动态性 OC的运行时系统(Runtime System)提供了丰富的动态特性,包括类与对象的创建、消息发送与转发、方法的动态添加与替换、属性的动态合成等。通过使用运行时库提供的API,可以在运行时获取和操作类与对象的信息,实现各种动态性的功能。 我对 Runtime 的理…...
画图实战-Python实现某产品全年销量数据多种样式可视化
画图实战-Python实现某产品全年销量数据多种样式可视化 学习心得Matplotlib说明什么是Matplotlib?Matplotlib特性Matplotlib安装 产品订单量-折线图某产品全年订单量数据数据提取和分析绘制折线图 产品订单&销售额-条形图某产品全年订单&销售额数据绘制条形…...
YOLOv9详解
1.概述 在逐层进行特征提取和空间转换的过程中,会损失大量信息,例如图中的马在建模过程中逐渐变得模糊,从而影响到最终的性能。YOLOv9尝试使用可编程梯度信息PGI解决这一问题。 具体来说, PGI包含三个部分,࿰…...
17网站一起做网店优势与劣势/客户关系管理系统
<?php header(Content-type: image/png);//图形的宽和高 $im_width 75; $im_height 25;//取随机的四位数 $nmsg ; for ($i 0; $i < 4; $i) {$nmsg . dechex(rand(0, 15)); }//创建图形区域 $im imagecreatetruecolor(75, 25);//填充图形背景色为白色 $white image…...
网站建设分工明细表/百度宣传推广费用
导读 这篇文章主要介绍了如何在 Python 中注释,帮助其他开发者理解你的代码,以及代码的目的。感兴趣的朋友可以了解下 在写 Python 代码的时候,一个很好的编码实践就是使得你的代码简洁,易懂。组织代码,设置变量&#…...
微信小程序如何生成二维码/化工seo顾问
对于创新,有很多偏见和误解。德鲁克在《创新与企业家精神》中指出,最常见的误解是认为大企业无法创新。大企业成了官僚作风和保守主义的代名词,事实上官僚和保守在小企业中同样普遍,只是因为企业太小,没有引起太多关注…...
阿里云ecs 做网站/河南关键词排名顾问
测试驱动开发与PHPUnitPhalApi推荐使用测试驱动开发最佳实践,并主要使用的是PHPUnit进行单元测试。 PHPUnit官网:https://phpunit.de,如需进行单元测试,请先安装PHPUnit。 以下是在PhalApi下简化后TDD步骤。定义接口服务的函数签名…...
小游戏网站审核怎么做/十大计算机培训学校
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数 输入: 121 输出: true 输入: -121 输出: false 解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。 输入: 10 输…...
自己做网站推广/官网制作公司
在本文中,我们将通过HLS自己动手实现一个AXI DMA,仿照的是官方AXI DMA IP核的直接模式,即给定发送起始地址和发送长度,以及接收起始地址和接收长度,就能进行一次DMA传输。 HLS设计 代码如下 dma.cpp #include"…...