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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【iOS】autoreleasepool

来说一下最近在了解的autoreleasepool吧&#xff0c;我们可能平时书写过许多脑残代码&#xff0c;其有很多的缺陷但是我们可能当时学的比较浅就也不太了解&#xff0c;就像下面这样的&#xff1a; for (int i 0; i < 1000000; i) {NSNumber *num [NSNumber numberWithInt…

matlab使用教程(10)—脚本和函数

1.概述 MATLAB 提供了一个强大的编程语言和交互式计算环境。您可以使用此语言在 MATLAB 命令行中一次输入一个命令&#xff0c;也可以向某个文件写入一系列命令&#xff0c;按照执行任何 MATLAB 函数的相同方式来执行这些命令。使用 MATLAB 编辑器或任何其他文件编辑器可以创建…

HCIP实验

实验题目如下&#xff1a; 实验拓扑如下&#xff1a; 实验要求如下&#xff1a; 【1】两个协议间进行多点双向重发布 【2】R7的环回没有宣告在OSPF协议中&#xff0c;而是后期重发布进入的 【3】解决环路&#xff0c;所有路径选择最优&#xff0c;且存在备份 实验思路如下&…

九、Spring 声明式事务学习总结

文章目录 一、声明式事务1.1 什么是事务1.2 事务的应用场景1.3 事务的特性&#xff08;ACID&#xff09;1.4 未使用事务的代码示例1.5 配置 Spring 声明式事务学习总结 一、声明式事务 1.1 什么是事务 把一组业务当成一个业务来做&#xff1b;要么都成功&#xff0c;要么都失败…

网络安全的相关知识点

网络安全威胁类型&#xff1a; 1.窃听&#xff1a;广播式网络系统。 2.假冒 3.重放&#xff1a;重复一份报文或者报文的一部分&#xff0c;以便产生一个被授权的效果。 4.流量分析 5.数据完整性破坏 6.拒绝服务 7.资源的非授权使用 8.陷门和特洛伊木马&#xff1a;木马病毒有客…

解数独(Java)

题目链接&#xff1a; 力扣 题目详情&#xff1a; 37. 解数独t编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只…

jmeter测试rpc接口-使用dubbo框架调用【杭州多测师_王sir】

1.基于SOAP架构。基于XML规范。基于WebService协议。特点:接口地址?wsdl结尾2.基于RPC架构&#xff0c;基于dubbo协议&#xff0c;thrift协议。SpringCloud微服务。3.基于RestFul架构&#xff0c;基于json规范。基于http协议(我们常用的都是这种&#xff0c;cms平台也是) Rest…

Git笔记--Ubuntu上传本地项目到github

目录 1--基本配置 2--本地上传 1--基本配置 ① 创建ssh-key cd ~/.sshssh-keygen -t rsa -C "邮箱地址"② 查看并关联ssh-key gedit id_rsa.pub 复制内容&#xff0c;在 GitHub 中依次点击 Settings -> SSH and GPG keys -> New SSH key&#xff0c;将 id…

实战指南:使用OpenCV 4.0+Python进行机器学习与计算机视觉

&#x1f482; 个人网站:【办公神器】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 1.背景2. 安装和配…

CTF Stegano练习之隐写初探

今天要介绍的是CTF练习中的Stegano隐写题型 。做隐写题的时候&#xff0c;工具是很重要的&#xff0c;接下来介绍一些工具。 1、TrID TrID是一款根据文件二进制数据特征进行判断的文件类型识别工具。虽然也有类似的文件类型识别工具&#xff0c;但是大多数都是使用硬编码的识…

【计算机视觉|生成对抗】生成对抗网络(GAN)

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;Generative Adversarial Nets 链接&#xff1a;Generative Adversarial Nets (nips.cc) 摘要 我们提出了一个通过**对抗&#xff08;adversarial&#xff09;**过程估计生成模型的新框架…

CentOS虚拟机更改屏幕锁屏时间

&#xff08;1&#xff09;点击“应用程序”&#xff0c;再点击“系统工具”&#xff0c;再点击“设置” &#xff08;2&#xff09; &#xff08;3&#xff09;在“设置”中点击“Privacy”&#xff0c;点击“锁屏”

Java基础入门篇——While循环(十二)

目录 一、循环结构语句 二、while循环语句 三、do-while循环语句 四、break使用 一、循环结构语句 在实际生活中经常会将同一件事情重复做很多次。例如&#xff0c;在做眼保健操的第四节轮刮眼眶时&#xff0c;会重复刮眼眶的动作&#xff1a;打乒乓球时&#xff0c;会重复…

新的里程碑!纪念正月十六工作室博客总访问量突破两百万

时值盛夏&#xff0c;清风徐徐&#xff0c;不觉间我们的博客访问量又迈入了新的里程碑——访问量突破两百万&#xff01; 总访问量突破百万&#xff1a; 个人成就&#xff1a; 记得上次突破重大里程碑还是去年夏天&#xff0c;那时我们重修岳阳楼&#xff0c;追往忆&#…

Python web实战之Django 的 RESTful API 设计详解

关键词: Python, Web 开发, Django, RESTful API 1 API的一些事儿 1.1 什么是API&#xff1f; API是应用程序编程接口&#xff08;Application Programming Interface&#xff09;的缩写。它是一种定义了不同软件组件之间交互方式的规范。API允许不同的应用程序之间进行通信和…

C语言内嵌汇编

反编译&#xff08;二进制文件或者so库&#xff09; objdump --help objdump -M intel -j .text -ld -C -S out > out.txt #显示源代码同时显示行号, 代码段反汇编-M intel 英特尔语法-M x86-64-C:将C符号名逆向解析-S 反汇编的同时&#xff0c;将反汇编代码和源代码交替显…

[C初阶笔记]P1

什么是C语言 1、机器语言&#xff08;二进制&#xff09;>汇编语言&#xff08;助记符&#xff09;>高级语言&#xff08;C、C等&#xff09; 2、c语言擅长底层软件开发&#xff08;操作系统、驱动程序&#xff09;&#xff0c;并不意味着不能开发其他。 C语言更贴近操作…

VS通过TCPIP与visionpro通讯

效果图 服务器端 visionpro配置服务器端&#xff0c;配置端口号、需要发送的数据等 客户端 vs编写代码接收数据 主要是复制的例程&#xff0c;到时候编写的时候可以借鉴。 using System; using System.Collections.Generic; using System.ComponentModel; using System.Dat…

《UNUX环境高级编程》(14)高级I/O

1、引言 2、 非阻塞I/O 系统调用分为两类&#xff1a;低速系统调用和其他系统调用。低速系统调用是可能会使进程永远阻塞的一类系统调用&#xff0c;包括&#xff1a; 如果某些文件类型&#xff08;如读管道、终端设备和网络设备&#xff09;的数据并不存在&#xff0c;读操作…

超导材料LK-99烧制工艺中高真空度及其气氛环境控制的解决方案

摘要&#xff1a;根据近期LK-99超导材料研究报道&#xff0c;我们分析此材料制备采用了真空烧结工艺。由于目前大部分复现研究所用的真空烧结技术和设备都非常简陋&#xff0c;使得LK-99的复现性很差。为此我们提出了真空度准确控制解决方案&#xff0c;其目的第一是实现烧结初…