SpringBoot 底层机制分析[上]

news2024/9/25 21:28:32

文章目录

  • 分析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
    */
    @Bean
    public 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/启动tomcat
                    this.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创建一个TomcatWebServlet
                    createWebServer.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.coloradmin.cn/o/854044.html

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

相关文章

ios启动崩溃保护

网传上个月下旬小红书因为配置问题导致连续性启动崩溃&#xff0c;最终只能通过紧急发版解决。对于冷启动崩溃保护的最容易查到的资料来源于微信读书团队的分享。 何为保护&#xff1f;要保护什么&#xff1f;该怎样保护&#xff1f;带着这几个疑问&#xff0c;一一谈一下个人的…

浅谈常态化压测

目录 一、常态化压测介绍 1.什么是常态化压测 2.为什么要进行常态化压测 3.常态化压测的价值 二、常态化压测实践 1.常态化压测流程介绍 2.首次进行常态化压测实践 2.1 准备阶段 2.2 执行阶段 2.3 调优阶段 2.4 复盘阶段 三、常态化压测总结 一、常态化压测介绍 1…

AI让分子“起死回生”:拯救抗生素的新希望

生物工程师利用人工智能(AI)使分子“起死回生”[1]。 为实现这种分子“复活”,研究人员应用计算方法对来自现代人类(智人)和我们早已灭绝的远亲尼安德特人和丹尼索瓦人的蛋白质数据进行分析。这使研究人员能够鉴定出可以杀死致病细菌的分子&#xff0c;从而促进研发用于治疗人类…

微信生态升级!小绿书来了!

如你所知&#xff0c;微信不只是一个聊天工具。一切从照片开始&#xff0c;你拍了一张照片&#xff0c;你就拥有了自己的相册&#xff0c;在“朋友圈”你可以了解朋友们的生活。如你所见&#xff0c;微信&#xff0c;是一个生活方式。不知不觉间&#xff0c;微信已经走过了 11个…

Docker的入门与使用

什么是Docker&#xff1f; docker官网 简介与概述 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#x…

C字符串与C++ string 类:用法万字详解(上)

目录 引言 一、C语言字符串 1.1 创建 C 字符串 1.2 字符串长度 1.3 字符串拼接 1.4 比较字符串 1.5 复制字符串 二、C字符串string类 2.1 解释 2.2 string构造函数 2.2.1 string() 默认构造函数 2.2.2 string(const char* s) 从 C 风格字符串构造 2.2.3 string(co…

通讯协议034——全网独有的OPC HDA知识一之聚合(三)时间加权平均

本文简单介绍OPC HDA规范的基本概念&#xff0c;更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化&#xff0c;以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…

使用一个python脚本抓取大量网站【2/3】

一、说明 我如何使用一个 Python 脚本抓取大量网站&#xff0c;在第 2 部分使用 Docker &#xff0c;“我如何使用一个python脚本抓取大量网站”统计数据。在本文中&#xff0c;我将与您分享&#xff1a; Github存储库&#xff0c;您可以从中克隆它;链接到 docker 容器&#xf…

软件定制开发平台:管好数据资源,降本提质!

在如今的发展时代&#xff0c;利用好优质的软件定制开发平台&#xff0c;定能给广大用户提高办公协作效率&#xff0c;创造可观的市场价值。作为服务商&#xff0c;流辰信息一直在低代码市场勤于钻研&#xff0c;不断努力&#xff0c;保持敏锐的市场眼光和洞察力&#xff0c;为…

Modelsim恢复编辑器的解决方案——只能将外部编辑器删除后,重新匹配编辑器

Modelsim恢复编辑器的解决方案——只能将外部编辑器删除后&#xff0c;重新匹配编辑器 1&#xff0c;Modelsim和Questasim是相互兼容的&#xff0c;配置的编辑器变成了sublime&#xff0c;且更换不了编辑器2&#xff0c;解决问题的方案&#xff0c;还是没得到解决3&#xff0c;…

Markdown和LaTex的学习

下载Typora Typora(免费版) 轻量级Markdown编辑器 - 哔哩哔哩 (bilibili.com) 部分编辑器需要进入设置 中开启特定的 Markdown 语法&#xff0c;例如 Typora 就需要手动开启 高亮 功能 Typora的使用&#xff1a; Typora中各种使用 - lyluoye - 博客园 (cnblogs.com) 标题 #…

数据库的存储过程、触发器、事件 实现(超精简)

一 存储过程 什么是存储过程 &#xff1a; 自己搜 和代码写的有什么区别&#xff1a; 没区别 为什么用存储过程&#xff1a; 快 例子 -- 创建 test名字的存储过程 CREATE PROCEDURE test(in idin INT) BEGIN-- 创建变量declare id int default 0;declare stopflag int defau…

爬虫015_python异常_页面结构介绍_爬虫概念介绍---python工作笔记034

来看python中的异常 可以看到不做异常处理因为没有这个文件所以报错了 来看一下异常的写法

【C++】C++回调函数基本用法(详细讲解)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【Flutter】【packages】simple_animations 简单的实现动画

package&#xff1a;simple_animations 导入包到项目中去 可以实现简单的动画&#xff0c; 快速实现&#xff0c;不需要自己过多的设置 有多种样式可以实现[ ] 功能&#xff1a; 简单的用例&#xff1a;具体需要详细可以去 pub 链接地址 1. PlayAnimationBuilder PlayAnima…

winform控件 datagridview分页功能

主要实现页面跳转、动态改变每页显示行数、返回首末页、上下页功能&#xff0c;效果图如下&#xff1a; 主代码如下&#xff1a; namespace Paging {public partial class Form1 : Form{public Form1(){InitializeComponent();}private int currentPageCount;//记录当前页行数…

ApplicationContext在Spring Boot中是如何创建的?

一、ApplicationContext在Spring Boot中是如何创建的&#xff1f; 1. SpringApplication ApplicationContextFactory有三个实现类&#xff0c;分别是AnnotationConfigReactiveWebServerApplicationContext.Factory、AnnotationConfigServletWebServerApplicationContext.Facto…

nginx动态加载配置文件的方法

1. main函数调用ngx_get_options函数 2. ngx_get_options(int argc, char *const *argv)中会解析用户输入命令。 case ‘s’: if (*p) { ngx_signal (char *) p; } else if (argv[i]) {ngx_signal argv[i];} else {ngx_log_stderr(0, "option \"-s\" requi…

将数组按照某个对象分类,结果值的json的值按照value递增排序

const arr [ { value: 532, lable: 1, type: “a” }, { value: 132, lable: 24, type: “b” }, { value: 432, lable: 13, type: “b” }, { value: 1812, lable: 5, type: “b” }, { value: 1932, lable: 8, type: “c” }, { value: 132, lable: 4, type: “a” }, { val…

CNN经典网络模型之GoogleNet论文解读

目录 1. GoogleNet 1.1 Inception模块 1.1.1 1x1卷积 1.2 辅助分类器结构 1.3 GoogleNet网络结构图 1. GoogleNet GoogleNet&#xff0c;也被称为Inception-v1&#xff0c;是由Google团队在2014年提出的一种深度卷积神经网络架构&#xff0c;专门用于图像分类和特征提取任…