带你Debug SpringApplication.run(MainApp.class, args) 看看SpringBoot 如何启动Tomcat

news2024/9/25 3:18:12

😀前言
本篇博文是关于SpringBoot 如何启动Tomcat的笔记,希望能够让你到SpringBoot印象深刻😊

🏠个人主页:晨犀主页
🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力😉😉

💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看🥰
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦😊

文章目录

  • 源码分析: SpringApplication.run()
    • Debug实现
      • Debug代码步骤
    • 😄总结

源码分析: SpringApplication.run()

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

Debug实现

image-20230807202043868

Debug代码步骤

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

😄总结

Debug的时候不用逐字逐句,我们只要挑重点进行分析。小编在上面的Debug步骤中已经把需要重点分析的地方做了备注。一些分析也在上面,希望能够帮助到您😄

😁热门专栏推荐
SpringBoot篇
SpringBoot 底层机制分析【Tomcat 启动+Spring 容器初始化+Tomcat 如何关联Spring 容器】【下】
SpringBoot容器–注解的使用
SpringBoot 自动配置–常用配置
SpringBoot 依赖管理和自动配置—带你了解什么是版本仲裁
Spring Boot介绍–快速入门–约定优于配置

文章到这里就结束了,如果有什么疑问的地方请指出,诸大佬们一起来评论区一起讨论😁
希望能和诸大佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞

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

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

相关文章

2023深圳杯A题完整代码模型

已更新深圳杯A题全部版本&#xff0c;文末获取&#xff01; 摘要 现代社会&#xff0c;随着生活方式的变化和工作压力的增大&#xff0c;慢性非传染性疾病日益成为威胁公众健康的主要问题。心脑血管疾病、糖尿病、恶性肿瘤及慢性阻塞性肺病等慢性病的发病率呈现出上升趋势。为…

Linux 安装部署Seata

标题&#xff1a;在Linux上安装部署Seata分布式事务解决方案 导语&#xff1a; Seata是一个开源的分布式事务解决方案&#xff0c;旨在解决分布式环境下的事务一致性问题。本文将为您介绍如何在Linux操作系统上安装和部署Seata&#xff0c;为您的分布式应用添加强大的事务支持。…

【【萌新的STM32学习-7】】

萌新的STM32学习-7 MAP 文件是MDK代码编译之后&#xff0c;产生的集程序&#xff0c;数据及IO 空间的一种映射列表文件 map 文件是编译器链接时生成的一个文件&#xff0c;它主要包含了交叉链接信息。通过.map 文 件&#xff0c;我们可以知道整个工程的函数调用关系、FLASH 和 …

ThingJS开发使用感受

封面来源于网络。 一、前言 1. 背景 出于为了实现有关厂区的数字孪生项目&#xff0c;断断续续使用ThingJS平台开发一年左右&#xff0c;做一个使用感受的总结。 2. 业务场景 开发一个基于厂区的数字孪生项目&#xff0c;基于ThingJS低代码开发的页面分为div3d、div2d结构&am…

【深度学习笔记】深度学习框架

本专栏是网易云课堂人工智能课程《神经网络与深度学习》的学习笔记&#xff0c;视频由网易云课堂与 deeplearning.ai 联合出品&#xff0c;主讲人是吴恩达 Andrew Ng 教授。感兴趣的网友可以观看网易云课堂的视频进行深入学习&#xff0c;视频的链接如下&#xff1a; 神经网络和…

【源码编译并安装RocketMQ Dashboard】

【源码编译并安装RocketMQ Dashboard】 一、环境说明二、源码编译并执行三、小结 一、环境说明 安装环境&#xff1a;虚拟机VMWare Centos7.6 Maven3.6.3 JDK1.8已经安装了RocketMQ-5.1.3 单Master集群&#xff0c;且使用Local模式部署&#xff0c;即Broker和Proxy同进程部署…

【Java】CAS数据交换流程

CAS的全称是&#xff1a; Compare And Swap(比较再交换)&#xff0c;它体现的一种乐观锁的思想&#xff0c;在无锁情况下保证线程操作共享数据的原子性。 CAS数据交换流程&#xff1a; 此时线程A和线程B都从主内存中拷贝了一份a100的共享变量到自己的工作内存中 线程A操作了变…

手势识别rtos小车(1)----手部识别

1.安装mediapipe库和cv2库 pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simplepip install Mediapipe0.8.9.1 -i https://pypi.tuna.tsinghua.edu.cn/simple some-package 这里我主要还是弄明白了这个pycharm编辑器和项目之间的关系&#xff0c;我在这里…

联盟CPS聚合联盟聚推客推广项目赚钱吗?揭秘有人月入10万+

大家好&#xff0c;我是巧匠&#xff01;最近因为忙碌于项目&#xff0c;都没来得及发布新的文章。不过现在&#xff0c;我给大家带来了一个新的项目玩法——那就是我们常说的淘客CPS系统。相信大家对“聚推客联盟”这个系统都有所了解。这个系统的模式真的很棒&#xff0c;让我…

nodejs登录生成token并验证

目录 一、基础概念 二、JTW 三、实践 一、基础概念 "token"&#xff1a; 是一个通用的术语&#xff0c;指代一种用于表示身份、权限或访问凭证的数据结构。它可以是一个字符串、数字或其他形式的数据。 主要用途&#xff1a; 身份验证&#xff1a;在身份验证过程…

ruoyi若依 组织架构设计--[ 角色管理 ]

ruoyi若依 组织架构设计--[ 角色管理 ] 角色新增后端代码 角色修改后端代码 角色查询角色删除角色分配数据权限后端代码 角色分配用户 角色新增 后端代码 有一点&#xff0c;我认为新增的时候&#xff0c;也需要修改redis中的权限。 角色修改 后端代码 因为修改了role_menu表了…

Database Comparer VCL for Delphi crack

Database Comparer VCL for Delphi crack 数据库比较器VCL比较并同步许多流行数据库的数据库结构(元数据)和表数据。支持的数据库列表不断更新&#xff0c;包括InterBase、FireBird、MySQL、MSSQL、Oracle、Sybase、PostgreSQL、DB2、PervasiveSQL、MSAccess、Paradox、DBASE以…

Java多线程(2)---线程控制和线程安全的详细讲解

目录 前言 一.线程控制方法 1.1启动线程--start() 1.2线程睡眠---sleep()方法 1.3中断线程--interrupt() 方法 1.4等待线程---join() 二.线程安全 2.1数据不安全---数据共享 ⭐不安全的演示和原因 ⭐不安全的处理方法 ⭐synchronize的使用 2.2数据不安全---内存可见…

条条大路通罗马系列—— 使用 Hiredis-cluster 连接 Amazon ElastiCache for Redis 集群

前言 Amazon ElastiCache for Redis 是速度超快的内存数据存储&#xff0c;能够提供亚毫秒级延迟来支持 实时应用程序。适用于 Redis 的 ElastiCache 基于开源 Redis 构建&#xff0c;可与 Redis API 兼容&#xff0c;能够与 Redis 客户端配合工作&#xff0c;并使用开放的 Re…

【动态规划】最长上升子序列 LIS

算法提高课笔记。 目录 问题的开始&#xff1a;最长上升子序列思路代码 怪盗基德的滑翔翼题意思路代码 登山题意思路代码 友好城市题意思路代码 拦截导弹题意思路代码 导弹防御系统题意思路代码 最长公共上升子序列题意思路代码 问题的开始&#xff1a;最长上升子序列 原题链…

linux后台运行程序命令screen

前言 我们在服务器终端或者是使用ssh连接服务器的时候&#xff0c;需要长期后台运行项目&#xff0c;但是我们一关终端可能程序进程就会被kill掉了&#xff0c;我们之前学习过2>&1 &后台部署的命令&#xff0c;但是这样我们查看项目进程的时候还需要去查询运行的PI…

TartanVO: A Generalizable Learning-based VO 论文阅读

论文信息 题目:TartanVO: A Generalizable Learning-based VO 作者&#xff1a;Wenshan Wang&#xff0c; Yaoyu Hu 来源&#xff1a;ICRL 时间&#xff1a;2021 代码地址&#xff1a;https://github.com/castacks/tartanvo Abstract 我们提出了第一个基于学习的视觉里程计&…

Netty使用和常用组件辨析

Netty 使用和常用组件 简述 <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId <version>4.1.42.Final </version> <scope>compile</scope> </dependency> Netty 的优势 1 、 AP…

42. 疯狂爬取王者荣耀所有皮肤高清海报(文末源码)

目录 前言 目的 思路 代码实现 1. 导包&#xff0c;部署好环境 2. 伪装请求头 3. 访问英雄列表&#xff0c;获取英雄ID 4. 分别访问各英雄主页&#xff0c;查看图片详情 5. 写入本地文件夹&#xff08;文件夹自动命名&#xff09; 完整源码 运行效果 总结 前言 阔…

流量、日志分析分析

这周主要以做题为主 先找找理论看然后在buuctrf以及nssctf找了题做 了解wireshark Wireshark是一款开源的网络协议分析软件&#xff0c;具有录制和检查网络数据包的功能&#xff0c;可以深入了解网络通信中的传输协议、数据格式以及通信行为。Wireshark可以捕获发送和接收的数…