SpringBoot 底层机制分析[上]
文章目录
- 分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器】[上]
- 搭建SpringBoot 底层机制开发环境
- @Configuration + @Bean 会发生什么,并分析机制
- 提出问题:SpringBoot 是怎么启动Tomcat ,并可以支持访问@Controller
- 源码分析: SpringApplication.run()
分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器】[上]
搭建SpringBoot 底层机制开发环境
1、创建Maven 项目nlc-springboot



2、修改nlc-springboot\pom.xml , 导入相关依赖
<groupId>com.nlc</groupId>
<artifactId>nlc-springboot</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 导入springboot 父工程,规定的写法-->
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.3</version>
</parent>
<!-- 导入web 项目场景启动器,会自动导入和web 开发相关依赖,非常方便-->
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>
3 、创建nlc-springboot\src\main\java\com\nlc\springboot\MainApp.java
@SpringBootApplication
public class MainApp {public static void main(String[] args) {//启动SpringBoot 应用程序ConfigurableApplicationContext run = SpringApplication.run(MainApp.class, args);}
}
4、启动项目ok, 大家注意Tomcat 也启动了

@Configuration + @Bean 会发生什么,并分析机制
1 、创建nlc-springboot\src\main\java\com\nlc\springboot\bean\Dog.java
public class Dog {
}
2 、创建nlc-springboot\src\main\java\com\nlc\springboot\config\Config.java
@Configuration
public class Config {/*** 1. 通过@Bean 的方式, 将new 出来的Bean 对象, 放入到Spring 容器* 2. 该bean 在Spring 容器的name 就是方法名* 3. 通过方法名, 可以得到new Dog()* @return*/@Beanpublic Dog dog() {return new Dog();}
}
3 、Debug:nlc-springboot\src\main\java\com\nlc\springboot\MainApp.java, 看看容器中是否已经注入了dog 实例





4、底层机制分析: 仍然是我们实现Spring 容器那一套机制IO/文件扫描+注解+反射+集合+映射
提出问题:SpringBoot 是怎么启动Tomcat ,并可以支持访问@Controller
1 、创建nlc-springboot\src\main\java\com\nlc\springboot\controller\HiController.java
@RestController
public class HiController {@RequestMapping("/hi")public String hi() {System.out.println("hi i am HiController");return "hi i am HiController";}
}
2、完成测试, 浏览器输入http://localhost:8080/hi

3、问题: SpringBoot 是怎么内嵌Tomcat, 并启动Tomcat 的? =>追踪源码
源码分析: SpringApplication.run()
1、Debug SpringApplication.run(MainApp.class, args) 看看SpringBoot 是如何启动Tomcat 的.
2、我们的Debug 目标: 紧抓一条线, 就是看到tomcat 被启动的代码. 比如tomcat.start()

public class MainApp {public static void main(String[] args) {//启动springboot应用程序/项目//当我们执行run方法时,怎么就启动我们的内置的tomcat?//在分析run方法的底层机制的基础上,我们自己尝试实现ConfigurableApplicationContext ioc =SpringApplication.run(MainApp.class, args);/** 开始debug SpringApplication.run()* 1.SpringApplication.java* public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class[]{primarySource}, args);}** 2.SpringApplication.java:创建返回 ConfigurableApplicationContext 对象* public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {* return (new SpringApplication(primarySources)).run(args);* }** 3.SpringApplication.java* public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();ConfigurableApplicationContext context = null;this.configureHeadlessProperty();SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);this.configureIgnoreBeanInfo(environment);Banner printedBanner = this.printBanner(environment);context = this.createApplicationContext();//创建容器,严重分析context.setApplicationStartup(this.applicationStartup);this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);this.refreshContext(context);//严重分析,刷新应用程序上下文,比如:初始化默认配置/注入相关Bean/启动tomcatthis.afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);}listeners.started(context);this.callRunners(context, applicationArguments);} catch (Throwable var10) {this.handleRunFailure(context, var10, listeners);throw new IllegalStateException(var10);}try {listeners.running(context);return context;} catch (Throwable var9) {this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);throw new IllegalStateException(var9);}}* 4.SpringApplication.java:容器类型很多,会根据你的this.webApplicationType创建对应的容器* 默认this.webApplicationType 是SERVLET也就是web容器/可以处理servlet* protected ConfigurableApplicationContext createApplicationContext() {return this.applicationContextFactory.create(this.webApplicationType);}** 5.ApplicationContextFactory.java* ApplicationContextFactory DEFAULT = (webApplicationType) -> {try {switch (webApplicationType) {//如果想要更改可以通过配置文件更改想要创建的容器种类,但是目前本身就是web开发,没必要去改case SERVLET://默认进入这个分支,返回AnnotationConfigServletWebServerApplicationContext容器return new AnnotationConfigServletWebServerApplicationContext();case REACTIVE:return new AnnotationConfigReactiveWebServerApplicationContext();default:return new AnnotationConfigApplicationContext();}} catch (Exception var2) {throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);}};** 6.SpringApplication.java* private void refreshContext(ConfigurableApplicationContext context) {if (this.registerShutdownHook) {shutdownHook.registerApplicationContext(context);}this.refresh(context);//严重分析,真正执行相关任务}** 7.SpringApplication.java* protected void refresh(ConfigurableApplicationContext applicationContext) {applicationContext.refresh();}**8.ServletWebServerApplicationContext.java* public final void refresh() throws BeansException, IllegalStateException {try {super.refresh();//容器类型很多,走一下父类} catch (RuntimeException var3) {WebServer webServer = this.webServer;if (webServer != null) {webServer.stop();//如果出现异常就关闭服务,所以 super.refresh()里面肯定存在tomcat启动代码}throw var3;}}** 9.AbstractApplicationContext.java* public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);beanPostProcess.end();this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();//有点像模板模式,先回到父类做一些共同的工作,因为下面有很多容器都会做共同的工作,到真正要刷新的时候又通过动态绑定机制回到子类,再开始进行具体的刷新任务this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var10) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);}this.destroyBeans();this.cancelRefresh(var10);throw var10;} finally {this.resetCommonCaches();contextRefresh.end();}}}**10.ServletWebServerApplicationContext.Java* protected void onRefresh() {super.onRefresh();try {this.createWebServer();//创建webservlet 可以理解成创建指定 web服务-Tomcat} catch (Throwable var2) {throw new ApplicationContextException("Unable to start web server", var2);}}**11.ServletWebServerApplicationContext.Java* private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = this.getServletContext();if (webServer == null && servletContext == null) {StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");ServletWebServerFactory factory = this.getWebServerFactory();createWebServer.tag("factory", factory.getClass().toString());this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});//严重分析,使用TomcatServletWebServerFactory创建一个TomcatWebServletcreateWebServer.end();this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));} else if (servletContext != null) {try {this.getSelfInitializer().onStartup(servletContext);} catch (ServletException var5) {throw new ApplicationContextException("Cannot initialize servlet context", var5);}}this.initPropertySources();}* 12.TomcatServletWebServerFactory.java* public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}Tomcat tomcat = new Tomcat();//创建Tomcat对象File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");//做了一系列的设置tomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);tomcat.getService().addConnector(connector);this.customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);this.configureEngine(tomcat.getEngine());Iterator var5 = this.additionalTomcatConnectors.iterator();while(var5.hasNext()) {Connector additionalConnector = (Connector)var5.next();tomcat.getService().addConnector(additionalConnector);}this.prepareContext(tomcat.getHost(), initializers);return this.getTomcatWebServer(tomcat);//严重分析}* 13.TomcatServletWebServerFactory.java//做了一个校验创建TomcatWebServer* protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());}** 14.TomcatServletWebServerFactory.java* public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {this.monitor = new Object();this.serviceConnectors = new HashMap();Assert.notNull(tomcat, "Tomcat Server must not be null");this.tomcat = tomcat;this.autoStart = autoStart;this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;this.initialize();//分析方法,进行初始化和相应的启动}** 15.TomcatServletWebServerFactory.java* private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));synchronized(this.monitor) {try {this.addInstanceIdToEngineName();Context context = this.findContext();context.addLifecycleListener((event) -> {if (context.equals(event.getSource()) && "start".equals(event.getType())) {this.removeServiceConnectors();}});this.tomcat.start();//启动Tomcat监听this.rethrowDeferredStartupExceptions();try {ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());} catch (NamingException var5) {}this.startDaemonAwaitThread();} catch (Exception var6) {this.stopSilently();this.destroySilently();throw new WebServerException("Unable to start embedded Tomcat", var6);}}}*执行到this.tomcat.start();我们可以返回如下图位置查看context容器值*/System.out.println("hello ioc");

当我们刷新整个上下文时像config的配置类进去了,包括我们的Bean也进去了

相关文章:
SpringBoot 底层机制分析[上]
文章目录 分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器】[上]搭建SpringBoot 底层机制开发环境Configuration Bean 会发生什么,并分析机制提出问题:SpringBoot 是怎么启动Tomcat ,并可以支持访问C…...
电源控制--对数与db分贝
在控制理论中,"db"通常表示分贝(decibel)的缩写。分贝是一种用于度量信号强度、增益或衰减的单位。 在控制系统中,分贝常用于描述信号的增益或衰减。通常,增益以正数的分贝值表示,而衰减以负数的…...
LeetCode 1749. 任意子数组和的绝对值的最大值(前缀和)
题目: 链接:LeetCode 1749. 任意子数组和的绝对值的最大值 难度:中等 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, …, numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 … numsr-1 numsr) 。 请你找出 nums 中 和的绝对…...
python爬虫相关
目录 初识爬虫 爬虫分类 网络爬虫原理 爬虫基本工作流程 搜索引擎获取新网站的url robots.txt HTHP协议 Resquests模块 前言: 安装 普通请求 会话请求 response的常用方法 简单案例 aiohttp模块 使用前安装模块 具体案例 数据解析 re解析 bs4…...
PAT(Advanced Level) Practice(with python)——1023 Have Fun with Numbers
Code N int(input()) D_N 2*N # print(Yes)if len(str(D_N))>len(str(N)):print(No) else:for s in str(D_N):if s not in str(N) or str(D_N).count(s)!str(N).count(s):print("No")breakelse:print(Yes) print(D_N)...
springboot vue 初步集成onlyoffice
文章目录 前言一、vue ts1. 安装依赖2. onlyoffice组件实现(待优化)3. 使用组件4. 我的配置文件 二、springboot 回调代码1. 本地存储 三、效果展示踩坑总结问题1问题2 前言 对接onlyoffice,实现文档的预览和在线编辑功能。 一、vue ts …...
Win10语言设置 - 显示语言和应用语言
前言 Win10的语言设置可以设置显示语言和应用语言。其中,显示语言用于显示系统文字;应用语言用于应用程序显示文字。下文介绍如何设置。 显示语言 打开系统设置,选择时间和语言,如下图: 修改Windows显示语言即可更…...
RxJava的前世【RxJava系列之设计模式】
一. 前言 学习RxJava,少不了介绍它的设计模式。但我看大部分文章,都是先将其用法介绍一通,然后再结合其用法,讲解其设计模式。这样当然有很多好处,但我个人觉得,这种介绍方式,对于没有接触过Rx…...
sql 语句 字段字符串操作
substring_index() 函数 字符串截取 表达式:substring_index(column,str,count) 释义:截取字符串column,str出现从前往后数第count次,之前的所有字符 示例语句:SELECT substring_index(‘www.baidu.com’,‘.’,2) 结…...
【网络工程】网络流量分析工具 Wireshark
文章目录 第一章:WireShark介绍第二章:WireShark应用第三章:Wireshark 实战 第一章:WireShark介绍 Wireshark (前身 Ethereal):它是一个强大的网络封包分析软件工具 ! 此工具使用WinPCAP作为接口,直接与网卡…...
数据库总结
第一章绪论 一、数据库系统概述 1. 数据库的4个基本概念 1.数据:数据库中存储的基本对象,描述事物的符号记录。 2.数据库:长期储存在计算机内、有组织的、可共享的大量数据的集合。较小的冗余度、较高的数据独立性、易扩展性 3.数据库管…...
虹科方案 | 成都大运会进行时,保障大型活动无线电安全需要…
成都大运会 7月28日,备受关注的第31届世界大学生夏季运动会在成都正式开幕。据悉,这是全球首个5G加持的智慧大运会,也是众多成熟信息技术的综合“应用场”。使用基于5G三千兆、云网、8K超高清视频等技术,在比赛现场搭建多路8K摄像…...
【C语言】扫雷 小游戏
文章目录 一、游戏规则二、 代码逻辑三、游戏实现1. 游戏菜单设计2.设计雷区并随机布置雷(1) 设置雷区(2) 布置雷 3.排查雷 四、源码 一、游戏规则 1. 在9*9的小格子中,任意选取一个坐标(格子),选择后发现,如果没点中雷…...
Jmeter(六) - 从入门到精通 - 建立数据库测试计划(详解教程)
1.简介 在实际工作中,我们经常会听到数据库的性能和稳定性等等,这些有时候也需要测试工程师去评估和测试,因此这篇文章主要介绍了jmeter连接和创建数据库测试计划的过程,在文中通过示例和代码非常详细地介绍给大家,希望对各位小伙…...
swagger 3.0 学习笔记
引入pom <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency>配置 import io.swagger.models.auth.In; import io.swagger.v3.oas.annotati…...
07 |「异步任务」
前言 实践是最好的学习方式,技术也如此。 文章目录 前言一、进程与线程1、进程2、线程 二、实现 一、进程与线程 1、进程 进程(Process)是操作系统分配资源的基本单位,它是一个执行中的程序实例;每个进程都有自己独立的内存空间,不同进程的内存是相互独…...
LoRaWan网关设计之入门指南
快速开始 以下是在目标平台本身上构建和运行 LoRaWan网关 的三步快速入门指南。 第 1 步:克隆 网关源码库 git clone https://github.com/lorabasics/basicstation.git...
互联网电影购票选座后台管理系统源码开发
搭建一个互联网电影购票选座后台管理系统需要进行以下步骤: 1. 需求分析:首先要明确系统的功能和需求,包括电影列表管理、场次管理、座位管理、订单管理等。 2. 技术选型:选择适合的技术栈进行开发,包括后端开发语言…...
[ K8S ] yaml文件讲解
目录 查看 api 资源版本标签写一个yaml文件demo创建资源对象查看创建的pod资源创建service服务对外提供访问并测试//创建资源对象查看创建的service写yaml太累怎么办? Kubernetes 支持 YAML 和 JSON 格式管理资源对象 JSON 格式:主要用于 api 接口之间消…...
【《深入浅出计算机网络》学习笔记】第1章 概述
内容来自b站湖科大教书匠《深入浅出计算机网络》视频和《深入浅出计算机网络》书籍 目录 1.1 信息时代的计算机网络 1.1.1 计算机网络的各类应用 1.1.2 计算机网络带来的负面问题 1.2 因特网概述 1.2.1 网络、互联网与因特网的区别与关系 1.2.1.1 网络 1.2.1.2 互联网 …...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
