找回密码
立即注册
搜索
发回帖 发新帖

6486

积分

0

好友

814

主题
发表于 2026-4-11 03:06:14 | 查看: 345| 回复: 0

一、前言

前文回顾:

数据是怎么从网卡到socket的?

数据是怎么从socket到tomcat的servlet?

本文基于 Spring Boot 3.3.0 与 Spring Framework 6.1.8,旨在追溯一次请求从 Servlet 到 Spring MVC Controller 的完整脉络。我们将回答以下问题:

  1. 介绍Springboot内嵌tomcat如何产生的?
  2. DispatcherServlet 又是如何被注册到 Tomcat 中的?
  3. Controller 与 DispatcherServlet 如何建立联系?
  4. 最终, DispatcherServlet 又是怎样调用到目标 Controller 的?

二、数据准备

我们从最简单的 Spring Boot 启动类与一个普通的 Controller 开始

@SpringBootApplication
public class App{
 public static void main(String[] args){
  SpringApplication.run(App.class, args);
 }
}

@RestController
@RequestMapping("/testController")
public class TestController{

 @ResponseBody
 @GetMapping("/getMethod")
 public Map<String, Object> getMethod(){
  Map<String, Object> result = new HashMap<>();
  result.put("aa", 1);
  return result;
 }
}

三、核心组件的诞生轨迹

我们先通过一张流程图,俯瞰整个 Spring Boot 应用,特别是 Web 相关的核心组件是如何在启动过程中被创建和组装的。

Spring Boot 应用启动及 Web 核心组件装配流程图

1.ConfigurationClassPostProcessor的注册

  • 在web环境下, Springboot启动的时候, 会创建 AnnotationConfigServletWebServerApplicationContext 作为spring的容器(SpringApplication#run -> createApplicationContext())。
  • 它的构造过程中初始化了 AnnotatedBeanDefinitionReader,并注册了核心的 ConfigurationClassPostProcessor
  • ConfigurationClassPostProcessor 是一个 BeanDefinitionRegistryPostProcessor, 负责解析注解配置(@Component、@Configuration、@Import等)。
  • 随后是容器准备工作(prepareContext), 其中会创建主类(App)的beanDefinition注册到容器中。

到这里, 启动主类(App)和用于处理注解的 ConfigurationClassPostProcessor 的 BeanDefinition 就都注册到spring容器中了。

2. WebMvc 相关核心类的装配

2.1 自动装配机制启动

  • 在 Spring 容器刷新的过程中(AbstractApplicationContext#refresh), 有个 invokeBeanFactoryPostProcessors 步骤, 它调用了 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 方法, 它会扫描主类(App) 所在包以及子包下的所有类, 将满足条件的类生成它的beanDefinition, 其中包括 @Component (@RestController 也是一个 @Component)、@Configuration、@Import 引入的类等等...
  • 其中 App 类的 @SpringBootApplication 注解上有个 @EnableAutoConfiguration 注解, 它使用 @Import(AutoConfigurationImportSelector.class) 导入了 AutoConfigurationImportSelector, 这个类是自动装配的核心, 它通过spi加载了 META-INF/org.springframework.boot.autoconfigure.AutoConfiguration.import文件 中配置的类。
  • 其中在 spring-boot-autoconfigure 项目中的 ...AutoConfiguration.imports 文件中有关web相关的核心内容如下
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

2.2 DispatcherServlet与DispatcherServletRegistrationBean

DispatcherServletAutoConfiguration 注册了两个关键 Bean:

  • DispatcherServlet: 核心Servlet, 当作分发器
  • DispatcherServletRegistrationBean: 继承自 ServletContextInitializer,用于将前者注册到 Servlet 容器中 (这里打个标, 下面会用到)
@Configuration(proxyBeanMethods = false)
protected static class DispatcherServletConfiguration{

 @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
 public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties){
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        ...
        return dispatcherServlet;
    }
}

@Configuration(proxyBeanMethods = false)
protected static class DispatcherServletRegistrationConfiguration{

 @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
 public DispatcherServletRegistrationBean dispatcherServletRegistration(...){
        DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
                webMvcProperties.getServlet().getPath());
        registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);

        return registration;
    }

}

2.3 内嵌 Tomcat 工厂

ServletWebServerFactoryAutoConfiguration 引入 EmbeddedTomcat 配置类,定义了 TomcatServletWebServerFactory。 这是内嵌 Tomcat 的构建工厂,负责生成一个最小可运行的 Web 容器。

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
 ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ...})
public class ServletWebServerFactoryAutoConfiguration{
    ...
}

static class EmbeddedTomcat{
 @Bean
 TomcatServletWebServerFactory tomcatServletWebServerFactory()
        ...
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        return factory;
    }
}

2.4 RequestMappingHandlerMapping和RequestMappingHandlerAdapter

WebMvcAutoConfiguration 的内部类 EnableWebMvcConfiguration 创建了两个核心组件:

  • RequestMappingHandlerMapping:负责 URL 到方法的映射注册。
  • RequestMappingHandlerAdapter:负责方法的执行与参数、返回值解析。
@Bean
 public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
        @Qualifier(“mvcContentNegotiationManager”) ContentNegotiationManager contentNegotiationManager,
        @Qualifier(“mvcConversionService”) FormattingConversionService conversionService,
        @Qualifier(“mvcValidator”) Validator validator) {

    RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
    ...
    adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

 if (jackson2Present) {
        adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
        adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }
 ...
    adapter.setCallableInterceptors(configurer.getCallableInterceptors());
    adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

    return adapter;
}

@Bean
 @SuppressWarnings(“deprecation”)
 public RequestMappingHandlerMapping requestMappingHandlerMapping(...){

    RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
    initHandlerMapping(mapping, conversionService, resourceUrlProvider);
 ...
    return mapping;
}

到这里, DispatcherServlet, DispatcherServletRegistrationBean, RequestMappingHandlerAdapter, RequestMappingHandlerMapping, TomcatServletWebServerFactory 就会被 ConfigurationClassPostProcessor 处理生成 BeanDefinition 注册到容器中。

四、核心组件的运行时装配

1. SpringBoot 内嵌 Tomcat 的创建

在容器刷新 (AbstractApplicationContext#refresh) 的 onRefresh 阶段,ServletWebServerApplicationContext 会执行 createWebServer() 方法, 如下:

private void createWebServer(){
    ...
 // 这里就是上面EmbeddedTomcat中的的TomcatServletWebServerFactory
    ServletWebServerFactory factory = getWebServerFactory();
 // 这里创建了一个ServletContextInitializer的匿名内部类, 它很重要
 this.webServer = factory.getWebServer(getSelfInitializer());
    ...
}

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer(){
 return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {

 // servlet容器的准备工作; 这里将spring上下文(this)添加到了ServletContext中
    prepareWebApplicationContext(servletContext);

 // 这里将servletContext注入到spring容器中
    registerApplicationScope(servletContext);

 // 注册servlet环境的几个对象到spring容器中
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);

 // 这里有DispatcherServletRegistrationBean, 用来将DispatcherServlet注册到ServletContext中
 for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

public WebServer getWebServer(ServletContextInitializer... initializers){
    Tomcat tomcat = new Tomcat();

 // …创建 StandardEngine, StandardHost, StandardServer和StandardService, Connector等

    prepareContext(tomcat.getHost(), initializers);
 return getTomcatWebServer(tomcat);
}

protected void prepareContext(Host host, ServletContextInitializer[] initializers){
 // TomcatEmbeddedContext是StandardContext的子类
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
 // 这里就是server.servlet.context-path 配置的项目访问路径
    context.setPath(getContextPath());

 // 合并ServletContextInitializer
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
 // 添加StandardContext
    host.addChild(context);

    configureContext(context, initializersToUse);
}

protected void configureContext(Context context, ServletContextInitializer[] initializers){
 // 将ServletContextInitializer包装到TomcatStarter中,
 // 这里TomcatStarter也是一个ServletContainerInitializer
    TomcatStarter starter = new TomcatStarter(initializers);
 if (context instanceof TomcatEmbeddedContext embeddedContext) {
        embeddedContext.setStarter(starter);
        embeddedContext.setFailCtxIfServletStartFails(true);
    }
 // 添加到TomcatEmbeddedContext中(StandardContext的子类)
    context.addServletContainerInitializer(starter, NO_CLASSES);
    ...
}

总结代码部分

  • 使用 TomcatServletWebServerFactory 创建内嵌 tomcat (类比成 apache 的 tomcat 中的 Bootstrap 类)
  • 初始化 StandardServer、StandardService、Connector、StandardEngine 和 StandardHost 等组件
  • 创建 TomcatEmbeddedContext (即 StandardContext 的子类), 代表当前 Web 应用;以前是解析一个 war 包得到一个 StandardContext, Springboot 中就直接创建一个
  • 将所有 ServletContextInitializer (包含 DispatcherServletRegistrationBean) 封装进 TomcatStarter
  • 最终 TomcatStarter 注册到 TomcatEmbeddedContext, 等待启动时执行

2. DispatcherServlet 的注册

再来看看上面的 getTomcatWebServer 方法的调用 (getWebServer 方法的最后一句), 它直接运行了 start 方法, tomcat 开始启动。

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat){
 return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown){
    ...
    initialize();
}

private void initialize() throws WebServerException {
 // …
 // 开始运行tomcat咯, 底层是server.start(); 会触发initialization
 this.tomcat.start();
 // …
}

在前文《数据是怎么从socket到tomcat的servlet?》 介绍过 StandardContext 在启动 (start 方法) 的时候会调用 ServletContainerInitializer#onStartup 方法, 所以这里就会调用到上面代码中由函数式接口定义的 ServletContextInitializer 的 selfInitialize 方法。

private void selfInitialize(ServletContext servletContext) throws ServletException {

 // servlet容器的准备工作; 这里将spring上下文(this)添加到了ServletContext中
    prepareWebApplicationContext(servletContext);

 // 这里将servletContext注入到spring容器中
    registerApplicationScope(servletContext);

 // 注册servlet环境的几个对象到spring容器中
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);

 // 这里有DispatcherServletRegistrationBean, 用来将DispatcherServlet注册到ServletContext中
 for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

这里就会执行 DispatcherServletRegistrationBean#onStartup 把 DispatcherServlet 添加到 TomcatEmbeddedContext 中 (StandardContext 的子类)。

3. MVC 组件注入

RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 注入到 DispatcherServlet

Tomcat 在启动时 (StandardContext#start 方法) 会调用 ServletContainerInitializer#onStartup 方法, 并且随后会调用 Servlet 的 init 方法。而 DispatcherServlet 实现了该方法, 最终会走到 DispatcherServlet 的 onRefresh -> initStrategies 方法中 (这里有 webmvc 的 9 大组件, 只列举我们关心的两个):

protected void initStrategies(ApplicationContext context){
  ...
  initHandlerMappings(context);
  initHandlerAdapters(context);
  ...
 }

这里就把 WebMvcAutoConfiguration$EnableWebMvcConfiguration 中创建的 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 注入到 DispatcherServlet 中了。

4. 将 Controller 对应的方法注册到 RequestMappingHandlerMapping

RequestMappingHandlerMapping 实现了 InitializingBean, 在 afterPropertiesSet() 中:

  • 扫描 @Controller 与 @RequestMapping
  • 生成 RequestMappingInfo 与 HandlerMethod
  • 注册到 MappingRegistry 中
  • 形成 URL → 方法的完整映射表

到这里, Tomcat、DispatcherServlet 以及 Controller 对应的 mapping 就全部准备就绪,请求分发链路已经打通。

五、请求分发与执行

当请求 http://localhost:8080/app/testController/getMethod 抵达时, 数据流经以下关键步骤。下图清晰地展示了 DispatcherServlet 处理一个典型请求(参数为 requestBody, 返回 responseBody)的内部流程与组件交互。

Spring MVC DispatcherServlet 请求处理流程图

  1. DispatcherServlet 调用 RequestMappingHandlerMapping, 根据请求的 url 先获取到 RequestMappingInfo 和 HandlerMethod
  2. 匹配到拦截器, 构建成一个链式结构 HandlerExecutionChain
  3. 根据 HandlerMethod 找到 EnableWebMvcConfiguration 中创建的 RequestMappingHandlerAdapter
  4. 执行前置拦截器
  5. RequestMappingHandlerAdapter#handle 方法中把 handlerMethod 包装成 ServletInvocableHandlerMethod; 设置参数处理器, 返回值处理器
  6. 参数解析 (@ModelAttribute、@InitBinder、@ControllerAdvice 等生效)
  7. 执行目标 controller 的对应的方法
  8. 随后被返回值处理器处理接口返回值, ModelAndView 等
  9. 最后拦截器做后置处理

六、总结

现在回到开篇提出的四个问题,给出结论:

1. Spring Boot 内嵌 Tomcat 是如何产生的?
在应用启动时, AnnotationConfigServletWebServerApplicationContext 会在 onRefresh() 阶段调用 createWebServer(), 通过 TomcatServletWebServerFactory 创建一个内嵌的 Tomcat。 该过程等价于传统 Tomcat 的 Bootstrap 启动逻辑,但以 Bean 的形式完成 Server、Service、Engine、Host、Context 等组件的初始化。

2. DispatcherServlet 是如何被添加到内嵌 Tomcat 中的?
DispatcherServlet 由 DispatcherServletAutoConfiguration 自动装配并注册为 Bean, 同时其对应的 DispatcherServletRegistrationBean 实现了 ServletContextInitializer 接口。当内嵌 Tomcat 启动并触发 onStartup() 回调时, 该注册器将 DispatcherServlet 动态添加到 TomcatEmbeddedContext 中。

3. Controller 又是如何与 DispatcherServlet 建立联系的?
WebMvcAutoConfiguration 提供的 EnableWebMvcConfiguration 在容器刷新阶段生成 RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter, DispatcherServlet 在初始化 (init 方法) 中注入这两个组件, 并通过前者扫描所有 @Controller 与 @RequestMapping, 完成请求路径与处理方法的注册。

4. DispatcherServlet 又是怎样调用到目标 Controller 的?
请求进入后, DispatcherServlet 首先由 HandlerMapping 定位目标方法 (HandlerMethod), 再交由 HandlerAdapter 调用, 经过参数绑定、拦截器链、返回值解析等环节, 最终将执行结果封装为响应对象返回客户端。理解这套复杂的 后端 请求处理机制,对于深入掌握 Spring MVC 和进行有效的故障排查至关重要。如果你有更深入的见解或疑问,欢迎在 云栈社区 与更多开发者交流讨论。




上一篇:2024金九银十Java面试高频真题28道(附场景与解析思路)
下一篇:IDN域名钓鱼攻击原理解析:同形异义字符的隐秘欺诈
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-11 04:14 , Processed in 0.090636 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表