从源码看世界:tomcat如何在spring boot中启动

Spring boot之所以如此受欢迎,不仅自动完成了大量配置,还内嵌了web容器tomcat,开箱即用。今天我们一起从源码中了解下tomcat是如何在spring boot中启动的。

首先说明下,网上很多文章在介绍这个主题时都是直接告诉读者在XXX类的XXX方法启动的,或者是第几行加了注释说明其作用,这样虽然十分直观快速了解到启动原理,但这只是“授之于鱼”,下次遇到其它知识点又不懂了。这里给大家“授之于渔”,也是我平时学习和排查问题的方法,就是 日志+debug 了!

当启动一个spring boot web项目时,会打印以下日志:

up-89a5f196cd292c55fe597a07504902a7d90.png

从日志中的tomcat关键词,可以看出是TomcatWebServer来启动tomcat的,于是进入这个类,在打印的日志行上打断点,重新启动spring boot就可以看到整个调用琏啦:

up-55bdd14d88aab90c6a17aaff543091ee60c.png

至此,我们就能找到tomcat的启动位置了。

然后我们来看看具体的启动过程:首先tomcat启动是在spring boot refresh步骤中开始的,而具体的方法在org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh,在里面调用了方法createWebServer

    private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		// ……
	}

这里首先创建ServletWebServerFactory对象,创建过程很简单,只是找出ServletWebServerFactory的实现类,但其实现类不止一个:

 

up-2eba0da2e728e36d77179ff393158c2550f.png

可以看出,spring boot至少支持jetty、tomcat、undertow三种web容器,为何使用的是tomcat呢?可以从tomcat的依赖树中找到原因,通过 mvn dependency:tree或者ide的maven插件show dependency:tree显示项目的依赖树,从中再查看tomcat的依赖关系:

[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.2.4.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-tomcat:jar:2.2.4.RELEASE:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.30:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.30:compile
[INFO] |  |  \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:9.0.30:compile

可以看出tomcat是在·spring-boot-starter-web引用的,在其pom文件中可以找到tomcat的dependency,并且在description也说明了原因:

<description>Starter for building web, including RESTful, applications using Spring
		MVC. Uses Tomcat as the default embedded container</description>

回到代码当中,在TomcatServletWebServerFactory#getWebServer就将tomcat初始化完成了,之后在ServletWebServerApplicationContext#finishRefresh将其启动起来

彩蛋:spring boot是如何支持多种不同的容器

为了方便地支持不同的容器,自然想到使用接口定义统一行为,翻看TomcatServletWebServerFactory的代码可以看到springboot的确也是这样做的。getWebServer返回的是WebServer对象,正是它定义了所有容器的通用行为:

public interface WebServer {
    // 启动容器
    void start() throws WebServerException;
    // 停止容器
    void stop() throws WebServerException;
    // 获取监听端口
    int getPort();
}

同样,创建WebServer工厂当然也有接口定义——ServletWebServerFactory:

public interface ServletWebServerFactory {
	WebServer getWebServer(ServletContextInitializer... initializers);
}

在前面的介绍中已经了解过ServletWebServerFactory的实现类,其中就有一个抽象实现类AbstractServletWebServerFactory,它里面实现了session、contextPath、ServletContextInitializer等容器的通用方法,这样tomcat、jetty等容器工厂实现类只需继承这个抽象类即可拥有通用能力,无需重复实现,这种设计也就是常说的 模板模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值