找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5091

积分

0

好友

709

主题
发表于 2 小时前 | 查看: 2| 回复: 0

一、前言

前文回顾:

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

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

本文基于 Spring Boot 3.3.0Spring 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)和用于处理注解的 ConfigurationClassPostProcessorBeanDefinition 就都注册到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;
}

到这里, DispatcherServletDispatcherServletRegistrationBeanRequestMappingHandlerAdapterRequestMappingHandlerMappingTomcatServletWebServerFactory 就会被 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 类)
  • 初始化 StandardServerStandardServiceConnectorStandardEngineStandardHost 等组件
  • 创建 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 方法, 所以这里就会调用到上面代码中由函数式接口定义的 ServletContextInitializerselfInitialize 方法。

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#onStartupDispatcherServlet 添加到 TomcatEmbeddedContext 中 (StandardContext 的子类)。

3. MVC 组件注入

RequestMappingHandlerMappingRequestMappingHandlerAdapter 注入到 DispatcherServlet

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

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

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

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

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

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

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

五、请求分发与执行

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

Spring MVC DispatcherServlet 请求处理流程图

  1. DispatcherServlet 调用 RequestMappingHandlerMapping, 根据请求的 url 先获取到 RequestMappingInfoHandlerMethod
  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 中的?
DispatcherServletDispatcherServletAutoConfiguration 自动装配并注册为 Bean, 同时其对应的 DispatcherServletRegistrationBean 实现了 ServletContextInitializer 接口。当内嵌 Tomcat 启动并触发 onStartup() 回调时, 该注册器将 DispatcherServlet 动态添加到 TomcatEmbeddedContext 中。

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

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




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

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

GMT+8, 2026-4-11 05:35 , Processed in 0.838775 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

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