文章目录

  • 一、SpringMVC
  • 二、Web容器选择
  • 2.1 代码依赖
  • 2.2 ServletWebServerFactoryConfiguration
  • 三、DispatcherServlet初始化
  • 3.1 装配过程
  • 3.2 加载到Servlet上下文
  • 3.2.1 回调TomcatStarter的onStartup
  • 3.2.2 执行初始化器
  • 3.3 执行init方法


一、SpringMVC

先解释下MVC,MVC是一种WEB开发的分层思想。
MVC分层:

  • Model:提供数据读写操作的业务模型
  • View:面向用户的视图层,根据处理后的模型数据渲染可视化页面
  • Controller:用于接收用户请求和操作数据的控制层

SpringMVC是Spring框架里的一个模块,实现了MVC这一分层思想。SpringMVC分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。下面看下在SpringBoot中,MVC是怎么实现的

二、Web容器选择

我们知道前端提交的Http请求,需要Web容器进行处理,例如Nginx、Apache。但是他们只能处理静态资源的请求,如果要把动态数据从数据库取出来返回,就不行了。Java规定处理请求Http请求需要遵循Servlet规范,Servlet容器就是Servlet规范的实现。请求流程大致如下图

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring


SpringBoot支持Tomcat、Jetty、Undertow三种Servlet容器,它们同时具备Web容器的功能,因此他们也是Web容器。那么哪个才是SpringBoot真正使用的Web容器呢?

2.1 代码依赖

pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
	<dependency>
	    <groupId>org.springframework.boot</groupId>
	    <artifactId>spring-boot-starter-web</artifactId>
	    <!-- 版本号继承至spring-boot-starter-parent,spring-boot-dependencies -->
	</dependency>

在pom.xml中引入spring-boot-starter-web的依赖,SpringBoot就好自动帮我们装配好WebMVC需要的Bean,最重要的就是DispatcherServlet。在SpringMVC单独使用时,需要手动配置一个DispatcherServlet,它实现了Servlet接口,就是一个Servlet。

2.2 ServletWebServerFactoryConfiguration

run的启动流程中讲过,Web容器的启动发生在,Spring容器启动流程中的onRefresh()方法。onRefresh会调用createWebServer(),getWebServerFactory从容器中取出ServletWebServerFactory,它是通过配置类ServletWebServerFactoryConfiguration被容器加载的。ServletWebServerFactory最终产生Web容器

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring_02


SpringBoot的自动装配读取的是spring.factories里面的配置类,看下spring-boot-autoconfigure的该文件里面

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring_03


没有ServletWebServerFactoryConfiguration,但是有一个ServletWebServerFactoryAutoConfiguration

spring boot 一个controller是不是一个线程 springboot中controller层原理_tomcat_04


在ServletWebServerFactoryAutoConfiguration上Import了ServletWebServerFactoryConfiguration的三个内部类。先看下EmbeddedTomcat

spring boot 一个controller是不是一个线程 springboot中controller层原理_mvc_05


它也是个配置类同样会被自动装配,但是它有个条件,需要Tomcat相关的类,才产生TomcatServletWebServerFactory,它实现了ServletWebServerFactory接口。也就是说,如果引入了Tomcat相关的依赖,前面提到的getWebServerFactory就能获取到TomcatServletWebServerFactory,用它来产生一个Web容器

再看下EmbeddedJetty

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring boot_06


和EmbeddedTomcat一样的原理,但是这块没有依赖Jetty相关的包,所以报红了,也就不会产生JettyServletWebServerFactory。EmbeddedUndertow也是一样的,就不看了。那么tomcat相关的依赖在哪呢?

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring boot_07


就是spring-boot-starter-web默认依赖了tomcat。如果想改用jetty,只需添加jetty的依赖,并将tomcat的依赖排除。如果两个依赖都存在会发生什么呢?进入getWebServerFactory

spring boot 一个controller是不是一个线程 springboot中controller层原理_mvc_08


只要超过一个ServletWebServerFactory,就会报错。因此,得到最终结论:SpringBoot选择Tomcat作为默认的Web容器,同时也是Servlet容器。

三、DispatcherServlet初始化

Tomcat启动会加载所有的Servlet(包含DispatcherServlet),第一次请求时调用Servlet的生命周期函数init(),init()只执行一次。DispatcherServlet会控制整个请求的处理,所以又叫做前端控制器。先看看它的装配过程

3.1 装配过程

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring boot_09


DispatcherServletAutoConfiguration进行了DispatcherServlet、DispatcherServletRegistrationBean的装配,DispatcherServletRegistrationBean封装了DispatcherServlet。dispatcherServlet到这里只是添加到了Spring容器,怎么添加到Servlet容器呢?

3.2 加载到Servlet上下文

SpringBoot是基于Servlet3.1规范的,那么在这个规范中指出:如果要添加一个Servlet,不光可以在web.xml中进行配置,还可以通过代码的方式servletContext.addServlet(name, this.servlet);

3.2.1 回调TomcatStarter的onStartup

再看Tomcat的启动过程,进入createWebServer

spring boot 一个controller是不是一个线程 springboot中controller层原理_初始化_10


这行首先通过getSelfInitializer()得到一个初始化器

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring boot_11


这是一个lambda表达式,调用selfInitialize方法,这块的封装回调函数也可以借鉴

spring boot 一个controller是不是一个线程 springboot中controller层原理_mvc_12


lambda表达式实现的是ServletContextInitializer的onStartup,因此在调用onStartup方法时就会进入执行selfInitialize。回到createWebServer,进入factory.getWebServer();

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring_13


initializers就是前面的初始化器,看看它是怎么封装的,进入prepareContext(tomcat.getHost(), initializers);

spring boot 一个controller是不是一个线程 springboot中controller层原理_初始化_14


在TomcatServletWebServerFactory的prepareContext方法里面,先创建TomcatEmbeddedContext的实例context,context封装进了host对象。再处理初始化器。进入configureContext

spring boot 一个controller是不是一个线程 springboot中controller层原理_mvc_15


在这里先将初始化器封装到启动器TomcatStarter的实例starter里面,再将启动器封装到了context。可以看到初始化器经过层层封装是进入了tomcat对象。接着一直跳过,返回到getWebServer,跟进去

getTomcatWebServer(tomcat);–>return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());–>initialize();–>this.tomcat.start();

spring boot 一个controller是不是一个线程 springboot中controller层原理_tomcat_16


tomcat.start()里面是tomcat的源码就不看了,他里面维护了线程池,在子线程中启动tomcat。启动过程中,会回调TomcatStarter的onStartup方法。前面提到的TomcatStarter也是被封装进入了tomcat。我们直接进入onStartup方法

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring_17


看这个调用链,说明tomcat在子线程中回调了TomcatStarter的onStartup方法。

3.2.2 执行初始化器

spring boot 一个controller是不是一个线程 springboot中controller层原理_mvc_18


看名称就知道第三个lambda就是前面ServletWebServerApplicationContext的getSelfInitializer返回的。循环之后进入到了lambda实现方法selfInitialize里

spring boot 一个controller是不是一个线程 springboot中controller层原理_初始化_19


可以看到getServletContextInitializerBeans()返回了DispatcherServletRegistrationBean,他里面封装了DispatcherServlet

进入

beans.onStartup(servletContext);–>register(description, servletContext);–>registration = addRegistration(description, servletContext);

spring boot 一个controller是不是一个线程 springboot中controller层原理_初始化_20


DispatcherServlet被添加进Servlet上下文。

3.3 执行init方法

写一个HelloController

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello(){
        return "hello!";
    }
}

在浏览器访问http://localhost:8080/hello,进入init方法

spring boot 一个controller是不是一个线程 springboot中controller层原理_mvc_21


这个方法在DispatcherServlet的父类HttpServletBean里面,进入initServletBean();initServletBean()是模板方法,进入到子类FrameworkServlet的实现

initServletBean()–>initWebApplicationContext()–>onRefresh(wac);

onRefresh是一个模板方法,进入子类DispatcherServlet

spring boot 一个controller是不是一个线程 springboot中controller层原理_spring_22


执行完initStrategies,就完成了DispatcherServlet的初始化。具体细节自行研究