SpringBoot启动原理
背景
1> 大家都知道SpringBoot是通过main函数启动的,这里面跟踪代码到处都没有找到while(true),为什么启动后可以一直跑?
2> SpringBoot默认使用tomcat作为web容器。大家也可以通过在pom文件中exclusion掉tomcat,denpendency jetty 的方法来使用jetty。那SpringBoot是怎么做到在不同web容器之间切换的呢?
2> 传统的web容器比如jetty本质上是直接通过java start.jar 来启动,之后来加载spring上下文的,SpringBoot通过main函数是怎么来启动web容器的呢?
本文就这三个问题展开论述。
问题1分析
问题1很简单,启动后一直跑是因为启动了线程池。原理就是有非deamon的线程在跑。Java虚拟机规范定义要等所有用户线程都运行完才会退出。
所以这个原理就和下面启动线程池一样

程序员修炼之道教我们:不要假定,要证明。虽然jetty使用线程池是常识,我们也来跟踪下源码,看看线程池是在哪里初始化的:
org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory类里,创建Server的使用使用线程池作为初始化参数。然后创建了socket连接来监听端口。(对于socket连接有之前没接触过的,可以自己查一下。建议动手实践。《Java异常处理总结》这篇文章里有不错的简单小例子可以实操下。)

到这里,大家应该都明白了为什么启动后一直不停。但是又有疑问了:JettyServletWebServerFactory是个什么东东?
问题2分析
关于问题2,我们写个最简单的类来debug一下:

进入SpringAppication.run的源码可以看到,里面创建了一个context,默认是AnnotationConfigServletWebServerApplicationContext。一初始化,在Bean定义里就加载了spring开天辟地的5个Bean。

继续向下执行走到AbstractApplicationContext的refresh方法,执行到onRefresh时,你进入方法里发现实际上执行的是
ServletWebServerApplicationContext的onFresh

这里面实际只做了一件事:创建web服务。

进入这个方法,debug到getWebServerFactory

来看一下:

获取的正式JettyServletWebServerFactory。为啥不是TomcatServlet呢?ServletWebServerFactoryAutoConfiguration的源码很好的说明了这个问题。源码的大意是当tomcat依赖存在就用tomcat,不然就按顺序找jetty存不存在,不存在再找Undertow存不存在。找到了就返回这个bean作为Servlet的工厂类。
@Configuration
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {public ServletWebServerFactoryAutoConfiguration() {}@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new ServletWebServerFactoryCustomizer(serverProperties);}@Bean@ConditionalOnClass(name = {"org.apache.catalina.startup.Tomcat"})public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;public BeanPostProcessorsRegistrar() {}public void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;}}public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {if (this.beanFactory != null) {this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);}}private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);beanDefinition.setSynthetic(true);registry.registerBeanDefinition(name, beanDefinition);}}}
}
至此第二个问题也真相大白。
问题3分析
第三个问题是传统的web容器比如jetty本质上是直接通过java start.jar 来启动,之后来加载spring上下文的,SpringBoot通过main函数是怎么来启动web容器。
这个问题在前面问题分析过程中也给了很多线索。我们来回顾下:SpringApplication.run里会创建Spring的应用上下文,默认是AnnotationConfigServletWebServerApplicationContext。首先会加载Spring开天辟地的5个Bean。然后它初始化各种Bean工厂。
SpringBoot在ServletWebServerApplicationContext中重载了onRefresh方法,除了以前Spring默认的onRefresh方法外还增加了createWebServer方法,在这个方法中对Web容器进行了初始化工作。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring.boot.version}</version><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion><exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId><version>${spring.boot.version}</version><exclusions><exclusion><groupId>org.eclipse.jetty.aggregate</groupId><artifactId>jetty-all</artifactId></exclusion></exclusions>
</dependency>
因为选择servlet容器是类似于使用基于条件的注解方式。因为当exclusion掉tomcat后,只有jetty满足条件,所以会加载JettyServletWebServerFactory。
通过getWebServer方法会new一个WebServer对象,new对象的方法会调用initialize方法,在这个方法中会对容器进行初始化并启动。
而容器启动的基本原理就是创建个线程池和网络套接字。用线程去处理套接字读写的内容。
总结
文本用带有少许说明的三个问题开场展开论述,实际是使用了麦肯锡大法中的SCQA架构。
SCQA架构是金字塔模型里面突出的一个论述方法,即“情境(Situation)、冲突(Complication)、问题(Question)、答案(Answer)”。可以帮助我们在陈述事实时条理更为清晰、有效。
SCQA其实只是麦肯锡做了总结。这个方法李清照都在用:
昨夜雨疏风骤,浓睡不消残酒 (情境)
试问卷帘人,渠道海棠依旧(冲突)
知否,知否(问题)
应是绿肥红瘦(答案)
文章正文看似一步步回答问题,实际上在讲述怎样去看spring源码,了解spring原理的一个过程。即:带着问题去看,debug跟踪源码验证 的方法。
相关文章:
SpringBoot启动原理
背景 1> 大家都知道SpringBoot是通过main函数启动的,这里面跟踪代码到处都没有找到while(true),为什么启动后可以一直跑? 2> SpringBoot默认使用tomcat作为web容器。大家也可以通过在pom文件中exclusion掉tomcat,denpendenc…...
区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归时间序列区间预测
区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 进阶版 基础版 基本介绍 MATLAB实现QRBiLS…...
看一看吧,面试真的卷......
大家好,最近有不少小伙伴在后台留言,今年面试实在是太卷了,不知道从何下手! 不论是跳槽涨薪,还是学习提升!先给自己定一个小目标,然后再朝着目标去努力就完事儿了! 为了帮大家节约…...
Python——1
一、注释 (1)单行注释:#需要注释的内容(#) (2)多行注释:需要注释的内容(三引号) 二、变量及变量类型 1.变量 语法定义:变量名 变量值&#…...
bgp团体属性配置案例一
RouterA的配置 sysname RouterA interface GigabitEthernet1/0/0 ip address 192.168.0.1 255.255.255.0 interface LoopBack0 ip address 1.1.1.1 255.255.255.255 bgp 10 router-id 1.1.1.1 //Router ID,建议配置为LoopBack0的IP地址 peer 192.168.0.2 as-number …...
mybatis-plus配置日志实现方式
Mybatis-plus是一个基于Mybatis的强大框架,可以帮助开发者快速地开发高质量的数据库应用程序。Mybatis-plus提供了许多配置项,其中一个重要的配置项是log-impl。 log-impl配置项定义了Mybatis-plus的日志实现方式,有两种可选的方式ÿ…...
Cy5.5 NHS ester 氨基反应性荧光染料Cyanine5.5 -NHS
Cy5.5 NHS ester是一种常用的荧光探针,可用于细胞或组织的荧光标记,也可用于蛋白质或核酸的标记。Cy5.5 NHS ester的荧光峰位于近红外区域,波长为675nm,具有较强的荧光强度和较长的荧光寿命,适合于生物分子在体内或组织…...
深度思考:在 AI 时代,你会被放大一千倍的能力是什么?
Datawhale干货 作者:艾芙,复旦大学,百姓AI教育负责人 前言 大家晚上好,我是艾芙,百姓 AI 的 AI 教育负责人。 先做一下自我介绍,我是一个在技术圈和教育圈反复横跳的斜杠中年了。大约在 5 年前,…...
蚂蚁实时低代码研发和流批一体的应用实践
摘要:本文整理自蚂蚁实时数仓架构师马年圣,在 Flink Forward Asia 2022 流批一体专场的分享。本篇内容主要分为四个部分: 实时应用场景与研发体系低代码研发流批一体规划展望 点击查看原文视频 & 演讲PPT 一、实时应用场景与研发体系 蚂蚁…...
5 创建映射
5 映射 上边章节安装了ik分词器,如果在索引和搜索时去使用ik分词器呢?如何指定其它类型的field,比如日期类型、数 值类型等。 本章节学习各种映射类型及映射维护方法。 5.1 映射维护方法 1、查询所有索引的映射: GET…...
windows注册表参数(%1,%2,%v) windows注册表是不区分大小写的.
windows注册表是不区分大小写的. 参数 含义 %1 文件路径 %2 系统默认的打印机 %3 文件扇区 %4 端口 %D 文件路径 %L 文件长路径 %V 文件路径 %W 当前文件的父目录的路径 参考:https://blog.csdn.net/meng_suiga/article/details/79485855 ————…...
基于SpringBoot的大学生租房系统
背景 大学生租房系统设计的目的是建立一个高效的平台,采用简洁高效的Java语言与Mysql数据库等技术,设计和开发了本大学生租房系统设计。该系统主要实现了用户和房主通过系统注册用户,登录系统后能够编辑自己的个人信息、查看首页,…...
NetApp 利用适用于混合云的实时解决方案解决芯片设计方面的数据管理挑战
电子设计自动化 (EDA) 成本持续增加,而周期时间缩短。这些都为 EDA 设计带来了前所未有的挑战,对现代高性能工作流的需求变得从未如此巨大。 联想凌拓芯片设计行业存储解决方案及最佳实践 联想凌拓芯片行业数据存储与管理解决方案,针对EDA…...
Rust + WASM 入门
一、参考资料 参考官方技术文档 https://rustwasm.github.io/ 二、安装脚手架 cargo-generate # cargo-generate 用于快速生成 WASM 项目的脚手架(类似 create-react-app) cargo install cargo-generate 三、下载安装 wasm-pack.exe 打包工具 双击安装…...
【操作系统】内存空间
最小的操作系统Hello world 想要pmap这个进程,需要进程号 但是这个进程在启动的一瞬间就执行完了 用GDB把程序暂停下来,然后用pmap观察地址空间 用info inferiors得到gdb里的进程号 ro 可读 :只读数据 rx 可读可执行 :代码 rw 可…...
详解静态、动态代理以及应用场景
一篇不太一样的代理模式详解,仔细阅读,你一定会获取不一样的代理见解,而不是人云亦云。 查看了社区里关于代理模式描述,发现很多博客千篇一律甚至存在共性错误,写此文提出自己对代理的见解。 静态代理动态代理 JDKCGLi…...
ChatGLM-6B本地cpu部署
ChatGLM-6B是清华团队研发的机器人对话系统,类似ChatGPT,但是实际相差很多,可以当作一个简单的ChatGPT。 ChatGLM部署默认是支持GPU加速,内存需要32G以上。普通的机器无法运行。但是可以部署本地cpu版本。 本地部署,需…...
算法修炼之练气篇——练气七层
博主:命运之光 专栏:算法修炼之练气篇 前言:每天练习五道题,炼气篇大概会练习200道题左右,题目有C语言网上的题,也有洛谷上面的题,题目简单适合新手入门。(代码都是命运之光自己写的…...
vscode常用快捷方式
基本编辑 Ctrl X:剪切当前行或选定内容 Ctrl C:复制当前行或选定内容 Ctrl V:粘贴当前行或剪切板内容 Ctrl Z:撤销上一步操作 Ctrl Y:恢复上一步撤销的操作 Ctrl F:在当前文件中查找内容 Ctrl H&am…...
如何压缩mp3文件大小,5分钟学会4种方法
如何压缩mp3文件大小?我们在开车的时候都很喜欢听歌,一般歌曲库里的mp3文件都很多,小编的就有上千首。如果我们还想要增加更多mp3文件,有时候就会出现内存不足的情况啦。所以我们需要压缩mp3文件大小,这样才能在我们手…...
迪文串口屏通信协议详解:从5AA5帧头到变量地址,一篇看懂HEX指令怎么发
迪文串口屏通信协议逆向解析:从帧头到数据域的HEX指令全解构 第一次拿到迪文串口屏的HEX指令时,那一串5A A5 11 82 0001 BFAA C6F4...让我完全摸不着头脑。为什么有的指令长度固定,有的却变化多端?地址字段和数据字段究竟如何划分…...
OpenCore技术方案:老旧设备系统兼容性深度解析与性能优化评估
OpenCore技术方案:老旧设备系统兼容性深度解析与性能优化评估 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 1. 问题剖析:为何老旧Ma…...
3分钟搞定加密音乐:Unlock-Music浏览器解密终极指南
3分钟搞定加密音乐:Unlock-Music浏览器解密终极指南 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地址: https:/…...
GD32F4xx GPIO实战:用按键控制LED,详解输入输出配置与防抖处理
GD32F4xx GPIO实战:从按键消抖到LED控制的完整设计指南 在嵌入式开发中,GPIO(通用输入输出)是最基础却至关重要的外设模块。对于GD32F4xx系列微控制器而言,掌握GPIO的高效配置不仅关乎功能实现,更直接影响系…...
微信聊天记录年度报告怎么生成?实测这款工具,一键导出HTML还能做可视化分析
从数据到故事:用专业工具打造你的微信聊天年度可视化报告 微信聊天记录早已不只是简单的文字交流,它们承载着人际关系的发展脉络、重要时刻的见证以及日常生活的点滴。将这些碎片化的对话转化为结构化的年度报告,不仅能帮助我们回顾过去一年…...
Hunyuan-MT-7B企业部署案例:出海SaaS公司集成Pixel Language Portal构建内部翻译中台
Hunyuan-MT-7B企业部署案例:出海SaaS公司集成Pixel Language Portal构建内部翻译中台 1. 项目背景与挑战 随着全球化业务扩张,某出海SaaS公司面临多语言支持的核心痛点: 翻译需求激增:产品文档、用户界面、客服对话等需要支持3…...
避坑指南:深度相机与RGB相机标定中的5个常见错误
避坑指南:深度相机与RGB相机标定中的5个常见错误 在三维重建和增强现实开发中,深度相机与RGB相机的联合标定是基础却极易出错的关键环节。许多开发者投入大量时间调试标定结果,却因忽视了一些看似简单的细节而功亏一篑。本文将揭示五个最常被…...
从快捷菜单到设置项:Android 11电池功能全移除实战指南
Android 11企业级设备电池功能深度定制指南 在工业平板、自助终端等专用设备场景中,系统界面的精简与定制往往比通用功能更重要。想象一下,一台用于仓库管理的工业平板,电池状态显示不仅毫无意义,还可能引发不必要的用户困惑——…...
百度网盘秒传链接终极指南:网页版工具全平台免费使用教程
百度网盘秒传链接终极指南:网页版工具全平台免费使用教程 【免费下载链接】baidupan-rapidupload 百度网盘秒传链接转存/生成/转换 网页工具 (全平台可用) 项目地址: https://gitcode.com/gh_mirrors/bai/baidupan-rapidupload 还在为百度网盘文件分享的繁琐…...
Qt6.10.1 + QCustomPlot 2.1.1 串口绘图实战:从Qt5老项目迁移到新版本的完整踩坑记录
Qt6.10.1与QCustomPlot 2.1.1串口绘图项目迁移实战指南 当Qt5项目需要升级到Qt6时,许多开发者都会面临兼容性挑战。特别是那些涉及串口通信和数据可视化的项目,往往隐藏着不少"坑"。本文将带你完整走一遍从Qt5老项目迁移到Qt6.10.1的全过程&am…...
