SpringBoot启动原理

news2024/9/21 22:44:27

背景

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() {
    }

    @Bean
    public 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跟踪源码验证 的方法。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

113.【Vue-细刷-04】

Vue-03 (二十四)、浏览器存储(WebStorage)1.本地缓存(LocalStorage)(1). 模仿本地缓存-未用JSON转字符串(2).模拟本地缓存-使用JSON转字符串 2.会话缓存(Session Storage)(1).模拟会话缓存(2).会话缓存和本地缓存的区别(3).JSON转换与JSON解析 3.todos案列_本地缓存版(1).mount…

NVIDIA CUDA驱动安装

1 引言 因为笔记本电脑上运行Milvus图像检索代码&#xff0c;需要安装CUDA驱动。电脑显卡型号是NVIDIA GeForce GTX 1050 Ti Mobile, 操作系统是Ubuntu 20.04&#xff0c;内核版本为Linux 5.15.0-72-generic。 2 CUDA驱动测试 参考网上的资料&#xff1a;https://blog.csdn.…

车载测试ADAS-常用场景仿真软件

ADAS&#xff08;Advanced Driber Assistant System&#xff09;&#xff0c;高级驾驶辅助系统&#xff0c;先进驾驶辅 助系统&#xff0c;作用于辅助汽车驾驶&#xff0c;通过感知、决策和执行&#xff0c;帮助驾驶员察觉可能发生的危险&#xff0c;是提高安全性的主动安全技术…

数据结构与算法基础(王卓)(36):交换排序之快排【第三阶段:深挖解决问题】精华!精华!精华!!!重要的事情说三遍

目录 Review&#xff1a; 具体问题&#xff1a; 操作核心&#xff1a; 注&#xff1a; 操作分解&#xff1a; 操作实现&#xff1a; 问题&#xff08;1&#xff09;&#xff1a;进行不一样次数的 if / else 判断 问题&#xff08;2&#xff09;&#xff1a;通过判断条件…

Element-UI

目录 Layout 布局 按钮组件结合 el-icon 使用 单选框 复选框 日期组件 表格 分页 对话框 表单验证 Element-UI是由饿了么前端团队开发的一套基于Vue.js的桌面端组件库&#xff0c;包含了多个常用的UI组件&#xff0c;如按钮、输入框、表格、弹窗等&#xff0c;可以快速…

5.QT应用程序主窗口

本章代码见文末链接 主窗口框架 新建Qt Wisgets项目mymainwindow&#xff0c;类名默认MainWindow&#xff0c;基类默认QMainWindow 更改文字如图&#xff0c;如果中文无法直接输入&#xff0c;可以试试复制粘贴 “动作编辑器”中&#xff08;默认在右下角&#xff09;&…

AI数字人系统搭建源码

AI数字人系统的功能可以根据具体应用场景而定&#xff0c;以下是一些可能的功能&#xff1a; 语音识别和合成&#xff1a;将自然语言转换为机器可读的文本&#xff0c;或将机器生成的文本转换为自然语言的语音输出。 面部表情捕捉&#xff1a;利用摄像头等设备获取用户…

2023鲁大师评测沟通会:鲁大师尊享版登场、“鲁小车”正式上线

作为硬件评测界的“老兵”&#xff0c;鲁大师不仅有着十几年的硬件评测经验&#xff0c;并且一直都在不断地尝试、不断地推陈出新。在5月9日举行的“2023年鲁大师评测沟通会”上&#xff0c;鲁大师向大众展示了在过去一年间取得的成果。 PC业务迭代升级&#xff0c;鲁大师客户端…

day(2,3)-内核模块

内核模块上 主要内容 向内核添加新功能 内核模块基础代码讲解 内核模块多源文件编程 内核模块信息宏 一、向内核添加新功能 1.1 静态加载法&#xff1a; 即新功能源码与内核其它代码一起编译进uImage文件内 Kconfig是make menuconfig的界面配置文件 1.2动态加载法&am…

Yolov8改进---注意力机制:DoubleAttention、SKAttention,SENet进阶版本

目录 🏆🏆🏆🏆🏆🏆Yolov8魔术师🏆🏆🏆🏆🏆🏆 1. DoubleAttention 2. SKAttention 3.总结

2022-4-4基于单片机的MQ2烟雾传感器报警系统设计

基于单片机的MQ2烟雾传感器报警系统设计 源代码和仿真图及MQ2相关资料可在文末的链接中下载 该系统实现的功能&#xff1a;检测空气中的烟雾浓度并实现超阈值报警 该系统组成由&#xff1a; 单片机最小系统、ADC0832、MQ2烟雾传感器、LCD1602液晶模块、声光报警模块、按键模…

研究人员发现微软Azure API管理服务存在3个漏洞

微软Azure API管理服务中披露了三个新的安全漏洞&#xff0c;恶意行为者可能会滥用这些漏洞来访问敏感信息或后端服务。 据以色列云安全公司Ermetic称&#xff0c;这包括两个服务器端请求伪造(SSRF)漏洞和API管理开发人员门户中的一个不受限制的文件上传功能实例。 安全研究员…

【腾讯云 Finops Crane 集训营】Crane应用实战

文章目录 前言一、Crane是什么&#xff1f;二、Crane的特点三、Crane使用1、环境准备2、安装Crane3、访问dashboard4、页面展示5、功能应用 四、Crane的优势总结 前言 FinOps&#xff08;Financial Operations&#xff09;是一种管理云计算成本的方法&#xff0c;它强调将云计…

使用PyTorch构建神经网络,并使用thop计算参数和FLOPs

文章目录 使用PyTorch构建神经网络&#xff0c;并使用thop计算参数和FLOPsFLOPs和FLOPS区别使用PyTorch搭建神经网络整体代码1. 导入必要的库2. 定义神经网络模型3. 打印网络结构4. 计算网络FLOPs和参数数量5. 结果如下手动计算params手动计算FLOPs注意 使用PyTorch构建神经网络…

车联网强势发展下,有什么隐患?

通过新一代信息通信技术&#xff0c;车联网实现了汽车与云平台&#xff0c;车辆和汽车&#xff0c;道路&#xff0c;汽车和人以及内部的全方位网络链接。车联网使用传感器技术感知车辆的状态信息&#xff0c;并利用无线通信网络和现代智能信息处理技术的帮助实现交通智能化管理…

FPGA入门系列10--按键消抖

文章简介 本系列文章主要针对FPGA初学者编写&#xff0c;包括FPGA的模块书写、基础语法、状态机、RAM、UART、SPI、VGA、以及功能验证等。将每一个知识点作为一个章节进行讲解&#xff0c;旨在更快速的提升初学者在FPGA开发方面的能力&#xff0c;每一个章节中都有针对性的代码…

Redis高可用系列——Set类型底层详解

文章目录 概述intsetintset 和 hashtable 的转换为什么加入了listpackhashtable 的空间开销高hashtable 的碰撞概率高intset 、listpack和hashtable的转换 概述 在讲解set结构之前&#xff0c;需要先说明一下set结构编码的更替&#xff0c;如下 在Redis7.2之前&#xff0c;se…

Ansys Lumerical | CMOS - 光学仿真方法

通过使用更小的像素尺寸和更大的填充因子&#xff0c;基于CMOS图像传感器像素的数码相机系统的成本正在降低。但是&#xff0c;只有在不牺牲图像质量的情况下&#xff0c;CMOS像素尺寸减小才是可以接受的。随着CMOS像素尺寸的不断减小&#xff0c;图像信噪比降低&#xff0c;相…

《我命由我不由天》蔡志忠——笔记一

目录 简介 经典摘录 三岁决定一生 父母该什么时候放手 确定将来要成为什么 积极主动为目标而努力 叛逆是最伟大的创意 父亲给蔡志忠最大的影响是教会他两件事 价值观缺陷导致的后果 人有三个阶段 简介 作者 蔡志忠&#xff0c;李虹。 蔡志忠&#xff1a;漫画家、哲…

力扣-1769. 移动所有球到每个盒子所需的最小操作数

题目&#xff1a; 有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes &#xff0c;其中 boxes[i] 的值为 ‘0’ 表示第 i 个盒子是 空 的&#xff0c;而 boxes[i] 的值为 ‘1’ 表示盒子里有 一个 小球。 在一步操作中&#xff0c;你可以将 一个 小球从某个盒子移动到一个与…