springboot如何选择Web Server源码分析

300 阅读2分钟

WebServer的自动装配

Springboot处理一次http请求时需要依赖WebServer,本次研究也是基于如下的父Pom。

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
</parent>

创建这个项目时,我仅仅就是引入了web模块。


<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Springboot为我们自动内嵌了webServer。在server的选择上自动配置的方案上有三种选择,如下图可见TomcatServletWebServerFactory的Conditional条件是存在Servelet、Tomcat和UpgradeProtocol类,环境也不存在ServletWebServerFactory类型的bean。这样会自动注册生产TomcatServer实例的Bean工厂。反观其他两种服务容器Jetty和UnderTow,Conditional条件就不满足。

Tomcat何时实例化和启动?

在分析springboot项目启动过程中我们知道(不知道的可以从org.springframework.boot.SpringApplication#run开始debug,你会收获很大:)!),作为web应用在SpringApplication构造函数中,会确定WebApplicationType为org.springframework.boot.WebApplicationType#SERVLET,这个也是在run方法中用来创建应用容器时默认选择容器类型的参数。

tomcat实例化就是在这个容器刷新时,应用容器刷新时,会调用抽象父类AbstractApplicationContext的refresh方法,该方法定义了标准的容器实例化bean流程。同时又给予了子容器的自定义初始化指定bean的扩展点onRefresh模版方法。

AnnotationConfigServletWebServerApplicationContext容器的类继承关系如下图

其onRefresh方法继承自父类ServletWebServerApplicationContext,该方法有一个关键的方法就是createWebServer(),通过此方法实例化tomcat,正式启动是在finishRefresh方法中完成的。至此tomcat就开始监听特定的端口(默认8080)的http请求,将请求封装成Request交由特定的Servlet来处理,也就是接下来要说的DispatcherServlet。

补充:Tomcat内部容器大致分为Engine、Host、Context和Wrapper。这四个容器之间是父子关系,Engine容器包含Host,Host包含Context,Context包含Wrapper。层层调用,最终调用Wrapper,Wrapper的具体调用方法invoke中其实是为request创建ApplicationFilterChain,在这个过滤器链中调用servlet.service方法。(invoke方法很长,以后可以深究)。