Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >spring(2) - springboot内置Tomcat 启动过程

spring(2) - springboot内置Tomcat 启动过程

作者头像
惊羽-布壳儿
发布于 2022-06-15 13:33:15
发布于 2022-06-15 13:33:15
93000
代码可运行
举报
文章被收录于专栏:惊羽-布壳儿惊羽-布壳儿
运行总次数:0
代码可运行

1. 整体关系

图引自 : Tomcat容器,Servlet容器,Spring容器的包含关系

2. 源码分析(嫌长的可以拉倒最后看总结)

2.1 jre的启动

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java -jar app.jar

当在服务器执行该命令的时候 : 服务器去环境变量找到 "java" 对应的执行文件路径 (例如 windows中的 java.exe) --> 执行java.exe --> 按照启动参数,调用JNI 申请内存,初始化JVM ,调用类的加载器进行类的预加载(检查,校验,加载等) 将jdk自带的核心类库加载到方法区 --> 找到主类 --> 执行 main方法

2.2 springboot main方法的启动

2.1.1 主类分析
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class AppApplication {
    private static volatile boolean RUNNING = true;
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(AppApplication .class, args);
        System.out.println("启动成功");
        synchronized (AppApplication .class) {
            while (RUNNING) {
                try {
                    AppApplication .class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    SpringApplication.exit(context);
                }
            }
        }
    }
}

主要调用的是 ApplicationContext context = SpringApplication.run(AppApplication .class, args); 方法;

2.1.2 SpringApplication.run() 分析
2.1.2.1 进入 SpringApplication.run() 实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
 }
2.1.2.2 继续进入 run(new Class[]{primarySource}, args) 方法的实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return (new SpringApplication(primarySources)).run(args);
}
2.1.2.3 继续进入 (new SpringApplication(primarySources)).run(args) 中 run()方法的实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            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, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

我们发现 , springboot 主类启动后,主要做了几个步骤 :

  1. 初始化启动监控,并启动 StopWatch stopWatch = new StopWatch(); stopWatch.start();
  2. 初始化容器监听,并启动SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting(); 注意这两不一样,一个只用于watch启动过程,启动完就停止了 stopWatch.stop();一个是贯穿整个springboot的进程监听;
  3. 创建容器 context = this.createApplicationContext();
  4. 准备容器 this.prepareContext(...);
  5. 刷新容器 this.refreshContext(context);
  6. 刷新后操作 this.afterRefresh(context, applicationArguments);
  7. 关闭启动监控 stopWatch.stop();

我们知道,启动Tomcat一定在这几个步骤中, 在上述 3. 4. 5. 6. 几个步骤都有可能,根据经验,创建和准备spring容器过程中不太合理,因为只有准备好spring,再去启动Tomcat容器,这样代码分层更加科学,固我们从刷新容器方法入手,如果找不到,我们在可以尝试从其他方法找找

2.1.2.4 继续进入 this.refreshContext(context); 方法的实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

    }
2.1.2.5 继续进入 this.refresh(context); 方法的实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext)applicationContext).refresh();
    }
2.1.2.6 继续进入 ((AbstractApplicationContext)applicationContext).refresh(); refresh() 方法

```` 有三个实现类,很明显第一个 AbstractApplicationContext 是个抽象类,其余2个是不同的实现,看着ServletWebServerApplicationContext 比较眼熟,我们选择先看他的实现

2.1.2.7 进入 ServletWebServerApplicationContext的refresh()方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public final void refresh() throws BeansException, IllegalStateException {
        try {
            super.refresh();
        } catch (RuntimeException var2) {
            this.stopAndReleaseWebServer();
            throw var2;
        }
    }

我们可以看到,他只是调用了一下父类super.refresh();然后有异常,会停止webServer ,等等,还没启动webServer,怎么会停止呢? 应该是在 super.refresh(); 启动了webServer,终于看到了webServer的影子!! 那么赶紧进入 super.refresh();

2.1.2.7 进入ServletWebServerApplicationContext父类 AbstractApplicationContext的refresh()方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

没有明显创建webServer,那一定在其中某一步,根据方法名字,我们进入 this.onRefresh()方法,其他方法都是有具体意义,和创建webServer关系不大,可以后边在看

2.1.2.7 进入this.onRefresh();方法

```` 有6个实现类,因为我们是从 ServletWebServerApplicationContext 进入的父类,所以调用的时候会调用该子类的 onRefresh();方法,固选择进入 ServletWebServerApplicationContext的 onRefresh() 实现

2.1.2.8 进入 ServletWebServerApplicationContext的 onRefresh() 实现
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    protected void onRefresh() {
        super.onRefresh();

        try {
            this.createWebServer();
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start web server", var2);
        }
    }

终于 !!! this.createWebServer(); 创建webServer了,进去看看 怎么玩儿的吧

2.1.2.8 进入 this.createWebServer();方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            ServletWebServerFactory factory = this.getWebServerFactory();
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var4) {
                throw new ApplicationContextException("Cannot initialize servlet context", var4);
            }
        }

        this.initPropertySources();
    }

factory.getWebServer() 提供三种webServer工厂,当然,我们就知道 时根据配置文件指定的webServer来初始化,所以我们进入 TomcatServletWebServerFactory的getWebServer(..) 方法;

2.1.2.9 进入 TomcatServletWebServerFactory的getWebServer(..) 方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }

        Tomcat tomcat = new 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);
    }

Tomcat tomcat = new Tomcat(); 处,悬追一下,可以看到 Tomcat 确实是初始化的Apache的Tomcat

到这,就知道了 spring大概是怎么启动内嵌的Tomcat的~

3. 总结

我们发现 , springboot 主类启动后,主要做了几个步骤 :

  1. 初始化启动监控,并启动 StopWatch stopWatch = new StopWatch(); stopWatch.start();
  2. 初始化容器监听,并启动SpringApplicationRunListeners listeners = this.getRunListeners(args);listeners.starting(); 注意这两不一样,一个只用于watch启动过程,启动完就停止了 stopWatch.stop();一个是贯穿整个springboot的进程监听;
  3. 创建容器 context = this.createApplicationContext();
  4. 准备容器 this.prepareContext(...);
  5. 刷新容器 this.refreshContext(context);
  6. 刷新后操作 this.afterRefresh(context, applicationArguments);
  7. 关闭启动监控 stopWatch.stop();

在 步骤5. (刷新容器 this.refreshContext(context);)中, 根据配置文件中指定的webServer配置,使用webserVer工厂调用 Tomcat tomcat = new Tomcat() 进行tomcat的实例创建和启动;

后记

这是从spring启动内嵌Tomcat切入,从源码跟踪了一下,其中也涉及到了spring的启动过程,当然涉及的不全;spring启动的整体流程是什么 ? spring的二方,三方组件 是怎么自动装配的,这些还需要进一步研究,下一篇再写吧

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
云+精选发布上线,大咖评审云集
在2020年接近尾声时,我们发布上线了社区的新产品——云+精选,该产品将文章按照内容等多个维度,利用算法的执行使文章聚合成一篇篇清单的形式。无论是对于作者来说还是对于读者来说都是受益颇多的,因此从新产品上线起就广受社区用户好评。
腾讯云开发者社区
2020/12/22
4830
云+精选发布上线,大咖评审云集
论如何成为技术大牛,GitHub中国区前20名详解
本文根据Github公开API,抓取了地址显示China的用户,根据粉丝关注做了一个排名,分析前一百名的用户属性,剖析这些活跃在技术社区的牛人到底是何许人也!
IT派
2018/07/30
2.3K0
论如何成为技术大牛,GitHub中国区前20名详解
腾讯云+社区新产品—阅读清单正式发布啦
腾讯云+社区新产品—阅读清单正式发布啦,你曾经是否也苦恼文章一篇一篇毫无规律的杂乱排布,你曾经是否也希望能够专注地阅读一个领域内的知识点,构建系统的知识架构。这些问题阅读清单已帮你解决,阅读清单是一个文章聚合的产品,它根据文章的内容、标题、索引、关联性等多个维度,利用先进的算法,聚合凝练成清单的形式。只为让你,在技术领域遨游中享受极致专一的阅读体验;只为让你,在纷杂的世界中,能够在阅读中获得片刻的宁静。
IT架构圈
2020/12/16
6070
腾讯云+社区新产品—阅读清单正式发布啦
产品经理上线自查清单
如上图所示,自查清单就是上线前对上线要准备的物料(如app store的图片和文案)以及要和谁对接(如运营)的一个自我检查是否有遗漏的事项的清单。
靠谱先生
2018/09/10
2.2K0
产品经理上线自查清单
网红AI教师Siraj Raval彻底栽了!剽窃论文遭Jeff Dean等大牛抵制
网红AI研究员、号称拥有 100 万学生的 Siraj Raval,这次彻底栽了。
新智元
2019/10/15
5550
网红AI教师Siraj Raval彻底栽了!剽窃论文遭Jeff Dean等大牛抵制
【阅读清单】有奖内测体验活动
完成了内测体验的小伙伴,可以填写问卷,活动结束后我们将统一进行审核,审核结束就进行礼品发放。问卷链接:https://wj.qq.com/s2/7433898/ff57/
腾讯云开发者社区
2020/10/26
6K12
【阅读清单】有奖内测体验活动
技术创业道阻且长?16位大咖带你破解创业“八十一难”!
这是技术最好的时代。 99%的组织在其IT系统中使用了开源软件,开源文化带来的“开放、创新”推动了互联网的技术发展。而随着“新基建”政策的加速推进,以云计算、大数据等为代表的IT基础设施获得极大普及,企业的数字化转型进程也在不断加快。技术本身、技术人才,成为了这个时代聚光灯的焦点。 这是否是技术创业最好的时代? “互联网下半场”已经成为业界关注与讨论的焦点,时代浪潮中的创业者也隐隐感知到了技术创业的利好。然而,下一个风口上的技术尚不明朗,下一个资本青睐的赛道也未必清晰。 创业绝不是凭借一腔热血便可贸然
腾讯云DNSPod团队
2021/03/05
6260
三句话,让纳德拉给我687亿美元!微软收购动视暴雪争霸元宇宙
2021年9月21日,微软宣布以75亿美元收购Bethesda母公司ZeniMax Media。
新智元
2022/02/24
3990
三句话,让纳德拉给我687亿美元!微软收购动视暴雪争霸元宇宙
一场为期 2 天的技术之旅,XDC 2022 圆满收官!| Q推荐
每一项改变世界的技术,都是从一行简单的"Hello World"开启,正如每一位建设数字世界的架构师 /CTO 们,都是从代码初学者成长起来。 在时代中成长起来的稀土掘金技术社区,希望用优质的内容和先进的工具,帮助开发者快乐地成长,这是「稀土开发者大会」发起的初衷。 历经 4 个月的精心打磨,连接中外技术生态,包揽 18 大技术专题,与上百位海内外技术专家深度交流,在 120 多个充满着不确定性的日夜中找寻着确定,直到 2022 年 7 月 22、23 日,第二届稀土开发者大会 (XDC 2022) 成功上
深度学习与Python
2023/03/29
3370
一场为期 2 天的技术之旅,XDC 2022 圆满收官!| Q推荐
【最全】一键关注腾讯技术类公众号,变身技术大咖!(下)
都说腾讯福利待遇好,不过要想加入鹅厂,坚持学习是必须的。只有通过坚持不懈的学习和奋斗,才能给自己加分,加入大厂不再是奢望。 如何保持学习,不断进步呢?其实不难,主要有几个方面:第一,保持行业好奇心,关
腾讯高校合作
2018/06/12
2.6K0
GitHub 中国区前 100 名到底是什么样的人?
本文根据Github公开API,抓取了地址显示China的用户,根据粉丝关注做了一个排名,分析前一百名的用户属性,剖析这些活跃在技术社区的牛人到底是何许人也!
疯狂的技术宅
2019/03/28
2.9K0
GitHub 中国区前 100 名到底是什么样的人?
2018年VR/AR十大关键词(六):《Beat Saber》
编者按:临近年底,VRPinea又开始了2018年VR/AR十大关键词的盘点。今年VRPinea选择的关键词为5G、赋能、Magic Leap、虚拟偶像、《头号玩家》、《Beat Saber》、VR一体机、TOVC-TOB/TO G、行业变动、Facebook。其中,本期关键词为:《Beat Saber》。
VRPinea
2018/12/27
1.1K0
2018年VR/AR十大关键词(六):《Beat Saber》
复盘Notion从0到100亿美金的增长路径,给Global SaaS公司带来哪些启示?
“当每个人都在收缩的时候,我们该如何扩张?” 近年来硅谷最火热的PLG产品代表非Notion莫属,上文是联合创始人Akshay Kothari在今年7月接受Protocol访谈时提到的思考。 当时公司向老股东红杉和Index按照前轮百亿美金估值原价出售老股,这在各大SaaS公司估值腰斩和大规模裁员的新闻中显得十分“异类”,但足以让“信仰”Notion的用户和员工倍感振奋。随即公司启动了一项全球范围的宣传活动,在公交车站牌、建筑外墙和户外大屏等各处地方都逐渐布设广告。 图:Notion的线下广告,来自Pro
腾讯SaaS加速器
2022/09/14
1.2K0
复盘Notion从0到100亿美金的增长路径,给Global SaaS公司带来哪些启示?
当出海成为必选项,企业如何构建全场景全生态技术底座?| Q推荐
采访嘉宾 | 宋清晨 整理 | 任传英 “大航海”时代,企业出海成为业务增长不可忽视的方式,出海技术的敏感度、数据合规、跨国通信、不同地区用户对于产品的倾向等,一直是行业中热议的话题。 9 月 15 日,融云在“超浪 2022 社交泛娱乐出海嘉年华”活动中,与白鲸出海联合发布了《2022 社交泛娱乐出海白皮书》(以下简称《白皮书》),回顾了国内互联网出海的历史,并聚焦社交泛娱乐作出系统的行业洞察与数据解读,对企业出海的现状、痛点与挑战进行了深度剖析。 (白皮书限时免费开放,感兴趣长按识别下载) 当天晚
深度学习与Python
2023/03/29
3960
当出海成为必选项,企业如何构建全场景全生态技术底座?| Q推荐
神策数据荣获“2020 InfoQ 最佳技术社区驱动力奖”|腾讯SaaS加速器·学员动态
来源 |  腾讯SaaS加速器二期项目-神策数据 ---- 近日,在 InfoQ“开发者生态共创计划”中,神策数据凭借大数据领域的前沿实践积累和高质量的技术内容输出,荣获“2020 InfoQ 最佳技术社区驱动力”奖项。 作为大数据分析与营销科技服务提供商,神策数据自成立以来,始终秉承“把事情做到极致”的精神,不断调整产品方法论和整体信息架构,补充产品功能,完善产品易用性。 目前,神策数据已成功为证券、银行、保险、品牌零售、汽车、游戏、企业服务、在线教育、电商等 30+ 细分行业 1500 多
腾讯SaaS加速器
2021/02/25
2700
腾讯旅游做的小程序,让记账也能成为一种享受 | 知晓程序 · MINA 奖
这里是「知晓程序 · MINA 奖」的第 4 期。 生活在别处。 当内心感到倦怠、迷惘的时候,很多人会选择去远方重新发现自己。 如何留住旅行的回忆呢?除了照片、日记之外,你是否有想过,记账也能成为一种纪念方式。 本期 MINA 奖,知晓程序(微信号 zxcx0101)颁发给了「旅行小账本」。这款别出心裁的小程序,让记账成为了一种零负担的美的享受。 还不知道如何使用小程序?点击这里,手把手教你用! 打开你的「旅行小账本」 「小旅行账本」提供了一种全新的记账体验。 在这里,你可以为每一次旅行,单独创建一个精美
知晓君
2018/06/28
9850
微信小程序远程控制电脑屏幕
作者:德里克--腾讯前端工程师 @IMWeb前端社区 背景 宇宙第一PHP花叔最近家里在装修,安装了一个投影仪,把电脑的画面投影到墙上,通过操控电脑来切换投影的画面。有一天他突然问我,我们能不能通过一个微信小程序来远程控制投影屏幕呢?接着我们讨论了一下,确实可以,因为微信小程序有WebSocket API。WebSocket虽不是新技术,但它结合微信小程序这个新产品,未来将诞生出更多线上线下的应用场景,值得我们关注。于是做了下面这个简易的DEMO,利用微信小程序来远程控制电脑屏幕。 开发过程 1、WebSo
用户1097444
2022/06/29
2.1K0
微信小程序远程控制电脑屏幕
3万计算机配置清单,电脑组装知识网预算2万至3万元电脑主机推荐九代酷睿i9-9900K搭RTX2080Ti全能型高配电脑主机配置清单…
本文转自:http://www.dn010.com/peizhi/710.html
全栈程序员站长
2022/09/13
1.1K0
3万计算机配置清单,电脑组装知识网预算2万至3万元电脑主机推荐九代酷睿i9-9900K搭RTX2080Ti全能型高配电脑主机配置清单…
腾讯WeTest 2021 年度回顾
导语 2021年,以“5G”、“云计算”、“大数据”为代表的数字技术深度应用于各行各业,企业数字化转型发展趋势持续加速,业内对产品质量保障的需求亦随之不断增大。作为行业先进的质量云服务厂商,过去的一年里,腾讯WeTest先后推出了全链路压测、小程序异常监控、自动化兼容测试等多项服务与解决方案,在不断升级产品能力的同时,还积极投身行业标准和测试生态的共建,更获得了中国科学院、CMMI等多项权威认证等。 下面,就带大家一起回顾一下腾讯WeTest的2021。 01 持续精进 推陈出新 自2015年问世以来,腾
WeTest质量开放平台团队
2022/01/20
1.1K0
一点思考|为什么建议开源社区的技术交流使用邮件列表?
互联网行业的运营更是喜欢这一套玩法:组织线下 meetup 讲讲技术,引导大家加群;线上直播搞个抽奖,引导大家加群;新产品、新功能上线搞点拉新活动,还会引导大家加群。
绯绡
2022/05/18
5210
一点思考|为什么建议开源社区的技术交流使用邮件列表?
推荐阅读
相关推荐
云+精选发布上线,大咖评审云集
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验