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

SpringBoot 底层机制分析[上]

文章目录

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

分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器】[上]

搭建SpringBoot 底层机制开发环境

1、创建Maven 项目nlc-springboot

image-20230806220339342

image-20230806220348854

image-20230806220649600

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 也启动了

image-20230807083553129

@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 实例

image-20230807083855246

image-20230807083915165

image-20230807083930803

image-20230807083946142

image-20230807084010118

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

image-20230807201933629

3、问题: SpringBoot 是怎么内嵌Tomcat, 并启动Tomcat 的? =>追踪源码

源码分析: SpringApplication.run()

1、Debug SpringApplication.run(MainApp.class, args) 看看SpringBoot 是如何启动Tomcat 的.
2、我们的Debug 目标: 紧抓一条线, 就是看到tomcat 被启动的代码. 比如tomcat.start()

image-20230807202043868

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");

image-20230809102552590

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

image-20230809103055023

相关文章:

SpringBoot 底层机制分析[上]

文章目录 分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器】[上]搭建SpringBoot 底层机制开发环境Configuration Bean 会发生什么&#xff0c;并分析机制提出问题&#xff1a;SpringBoot 是怎么启动Tomcat &#xff0c;并可以支持访问C…...

电源控制--对数与db分贝

在控制理论中&#xff0c;"db"通常表示分贝&#xff08;decibel&#xff09;的缩写。分贝是一种用于度量信号强度、增益或衰减的单位。 在控制系统中&#xff0c;分贝常用于描述信号的增益或衰减。通常&#xff0c;增益以正数的分贝值表示&#xff0c;而衰减以负数的…...

LeetCode 1749. 任意子数组和的绝对值的最大值(前缀和)

题目&#xff1a; 链接&#xff1a;LeetCode 1749. 任意子数组和的绝对值的最大值 难度&#xff1a;中等 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, …, numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 … numsr-1 numsr) 。 请你找出 nums 中 和的绝对…...

python爬虫相关

目录 初识爬虫 爬虫分类 网络爬虫原理 爬虫基本工作流程 搜索引擎获取新网站的url robots.txt HTHP协议 Resquests模块 前言&#xff1a; 安装 普通请求 会话请求 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组件实现&#xff08;待优化&#xff09;3. 使用组件4. 我的配置文件 二、springboot 回调代码1. 本地存储 三、效果展示踩坑总结问题1问题2 前言 对接onlyoffice&#xff0c;实现文档的预览和在线编辑功能。 一、vue ts …...

Win10语言设置 - 显示语言和应用语言

前言 Win10的语言设置可以设置显示语言和应用语言。其中&#xff0c;显示语言用于显示系统文字&#xff1b;应用语言用于应用程序显示文字。下文介绍如何设置。 显示语言 打开系统设置&#xff0c;选择时间和语言&#xff0c;如下图&#xff1a; 修改Windows显示语言即可更…...

RxJava的前世【RxJava系列之设计模式】

一. 前言 学习RxJava&#xff0c;少不了介绍它的设计模式。但我看大部分文章&#xff0c;都是先将其用法介绍一通&#xff0c;然后再结合其用法&#xff0c;讲解其设计模式。这样当然有很多好处&#xff0c;但我个人觉得&#xff0c;这种介绍方式&#xff0c;对于没有接触过Rx…...

sql 语句 字段字符串操作

substring_index() 函数 字符串截取 表达式&#xff1a;substring_index(column,str,count) 释义&#xff1a;截取字符串column&#xff0c;str出现从前往后数第count次&#xff0c;之前的所有字符 示例语句&#xff1a;SELECT substring_index(‘www.baidu.com’,‘.’,2) 结…...

【网络工程】网络流量分析工具 Wireshark

文章目录 第一章&#xff1a;WireShark介绍第二章&#xff1a;WireShark应用第三章&#xff1a;Wireshark 实战 第一章&#xff1a;WireShark介绍 Wireshark (前身 Ethereal)&#xff1a;它是一个强大的网络封包分析软件工具 ! 此工具使用WinPCAP作为接口&#xff0c;直接与网卡…...

数据库总结

第一章绪论 一、数据库系统概述 1. 数据库的4个基本概念 1.数据&#xff1a;数据库中存储的基本对象&#xff0c;描述事物的符号记录。 2.数据库&#xff1a;长期储存在计算机内、有组织的、可共享的大量数据的集合。较小的冗余度、较高的数据独立性、易扩展性 3.数据库管…...

虹科方案 | 成都大运会进行时,保障大型活动无线电安全需要…

成都大运会 7月28日&#xff0c;备受关注的第31届世界大学生夏季运动会在成都正式开幕。据悉&#xff0c;这是全球首个5G加持的智慧大运会&#xff0c;也是众多成熟信息技术的综合“应用场”。使用基于5G三千兆、云网、8K超高清视频等技术&#xff0c;在比赛现场搭建多路8K摄像…...

【C语言】扫雷 小游戏

文章目录 一、游戏规则二、 代码逻辑三、游戏实现1. 游戏菜单设计2.设计雷区并随机布置雷(1) 设置雷区(2) 布置雷 3.排查雷 四、源码 一、游戏规则 1. 在9*9的小格子中&#xff0c;任意选取一个坐标&#xff08;格子&#xff09;&#xff0c;选择后发现&#xff0c;如果没点中雷…...

Jmeter(六) - 从入门到精通 - 建立数据库测试计划(详解教程)

1.简介 在实际工作中&#xff0c;我们经常会听到数据库的性能和稳定性等等&#xff0c;这些有时候也需要测试工程师去评估和测试&#xff0c;因此这篇文章主要介绍了jmeter连接和创建数据库测试计划的过程,在文中通过示例和代码非常详细地介绍给大家&#xff0c;希望对各位小伙…...

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 |「异步任务」

前言 实践是最好的学习方式&#xff0c;技术也如此。 文章目录 前言一、进程与线程1、进程2、线程 二、实现 一、进程与线程 1、进程 进程(Process)是操作系统分配资源的基本单位,它是一个执行中的程序实例&#xff1b;每个进程都有自己独立的内存空间,不同进程的内存是相互独…...

LoRaWan网关设计之入门指南

快速开始 以下是在目标平台本身上构建和运行 LoRaWan网关 的三步快速入门指南。 第 1 步:克隆 网关源码库 git clone https://github.com/lorabasics/basicstation.git...

互联网电影购票选座后台管理系统源码开发

搭建一个互联网电影购票选座后台管理系统需要进行以下步骤&#xff1a; 1. 需求分析&#xff1a;首先要明确系统的功能和需求&#xff0c;包括电影列表管理、场次管理、座位管理、订单管理等。 2. 技术选型&#xff1a;选择适合的技术栈进行开发&#xff0c;包括后端开发语言…...

[ K8S ] yaml文件讲解

目录 查看 api 资源版本标签写一个yaml文件demo创建资源对象查看创建的pod资源创建service服务对外提供访问并测试//创建资源对象查看创建的service写yaml太累怎么办&#xff1f; Kubernetes 支持 YAML 和 JSON 格式管理资源对象 JSON 格式&#xff1a;主要用于 api 接口之间消…...

【《深入浅出计算机网络》学习笔记】第1章 概述

内容来自b站湖科大教书匠《深入浅出计算机网络》视频和《深入浅出计算机网络》书籍 目录 1.1 信息时代的计算机网络 1.1.1 计算机网络的各类应用 1.1.2 计算机网络带来的负面问题 1.2 因特网概述 1.2.1 网络、互联网与因特网的区别与关系 1.2.1.1 网络 1.2.1.2 互联网 …...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...