Salmon的全栈知识 Salmon的全栈知识
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • SpringCloud Gateway
  • Apollo
  • Eureka
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • SpringCloud Gateway
  • Apollo
  • Eureka
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
npm

(进入注册为作者充电)

  • Spring框架

    • 传统Javaweb开发的困惑
    • IoC、DI和AOP思想提出
    • Spring框架的诞生
    • 基于xml的Spring应用
    • 基于注解的Spring应用
    • AOP 简介
    • 基于xml配置的AOP
    • 基于注解配置的AOP
    • 基于AOP的声明式事务控制
  • SpringMVC框架

    • Spring整合web环境
    • web层MVC框架思想与设计思路
    • SpringMVC简介
    • SpringMVC的请求处理
    • SpringMVC的响应处理
    • SpringMVC的拦截器
    • SpringMVC的全注解开发
    • SpringMVC的组件原理剖析
      • 1、前端控制器初始化
      • 2、前端控制器执行主流程
    • SpringMVC的异常处理机制
  • 《Spring生态》笔记
  • SpringMVC框架
Salmon
2025-07-23
目录

SpringMVC的组件原理剖析

# 1、前端控制器初始化

前端控制器DispatcherServlet是SpringMVC的入口,也是SpringMVC的大脑,主流程的工作都是在此完成的,梳理一下DispatcherServlet 代码。DispatcherServlet 本质是个Servlet,当配置了 load-on-startup 时,会在服务器启动时就执行创建和执行初始化init方法,每次请求都会执行service方法

DispatcherServlet 的初始化主要做了两件事:

  • 获得了一个 SpringMVC 的 ApplicationContext容器;

  • 注册了 SpringMVC的 九大组件。

image-20250729010128625

SpringMVC 的ApplicationContext容器创建时机,Servlet 规范的 init(ServletConfig config) 方法经过子类重写,最终会调用 FrameworkServlet 抽象类的initWebApplicationContext() 方法,该方法中最终获得 一个根Spring容器(Spring产生的),一个子Spring容器(SpringMVC产生的)

HttpServletBean 的初始化方法

public final void init() throws ServletException {
   this.initServletBean();
}

FrameworkServlet的initServletBean方法

protected final void initServletBean() throws ServletException {
   this.webApplicationContext = this.initWebApplicationContext();//初始化ApplicationContext
   this.initFrameworkServlet();//模板设计模式,供子类覆盖实现,但是子类DispatcherServlet没做使用
}

在initWebApplicationContext方法中体现的父子容器的逻辑关系

// 初始化 WebApplicationContext 是一个非常关键的步骤
protected WebApplicationContext initWebApplicationContext() {
    // 获得根容器,实际上就是通过 ContextLoaderListener 创建的 ApplicationContext
    // 如果配置了 ContextLoaderListener,则能获得根容器,否则为 null
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());

    // 定义 Spring MVC 产生的 ApplicationContext 子容器
    WebApplicationContext wac = null;

    if (wac == null) {
        // 创建 Spring MVC 的子容器,创建时将 rootContext 传递过去,实现父子容器关系
        wac = this.createWebApplicationContext(rootContext);
    }

    // 将 Spring MVC 产生的 ApplicationContext 子容器存储到 ServletContext 域中
    // key 名称格式是:org.springframework.web.servlet.FrameworkServlet.CONTEXT.<servletName>
    if (this.publishContext) {
        String attrName = this.getServletContextAttributeName();
        this.getServletContext().setAttribute(attrName, wac);
    }

    return wac;  // 通常方法会返回创建好的子容器
}

跟进创建子容器的源码

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
    // 实例化子容器 ApplicationContext,通常是 AnnotationConfigWebApplicationContext 或 GenericWebApplicationContext
    ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    
    // 设置传递过来的 ContextLoaderListener 创建的 rootContext 作为父容器,实现容器继承
    wac.setParent(parent);
    
    // 获取 web.xml 中配置的 Spring MVC 配置文件路径,如 "classpath:spring-mvc.xml"
    String configLocation = this.getContextConfigLocation();
    if (configLocation != null) {
        // 设置子容器的配置路径
        wac.setConfigLocation(configLocation);
    }
    
    // 初始化子容器,加载配置文件中的 Bean 定义并刷新容器
    this.configureAndRefreshWebApplicationContext(wac);
    
    // 返回初始化完成的子容器
    return wac;
}

子容器中的parent维护着父容器的引用

image-20250729010313861

父容器和子容器概念和关系:

  • 父容器:Spring 通过ContextLoaderListener为入口产生的applicationContext容器,内部主要维护的是applicationContext.xml(或相应配置类)配置的Bean信息;

  • 子容器:SpringMVC通过DispatcherServlet的init() 方法产生的applicationContext容器,内部主要维护的是spring-mvc.xml(或相应配置类)配置的Bean信息,且内部还通过parent属性维护这父容器的引用。

  • Bean的检索顺序:根据上面子父容器的概念,可以知道Controller存在与子容器中,而Controller中要注入Service时,会先从子容器本身去匹配,匹配不成功时在去父容器中去匹配,于是最终从父容器中匹配到的UserService,这样子父容器就可以进行联通了。但是父容器只能从自己容器中进行匹配,不能从子容器中进行匹配。

注册 SpringMVC的 九大组件,在初始化容器initWebApplicationContext方法中执行了onRefresh方法,进而执行了初始化策略initStrategies方法,注册了九个解析器组件

// DispatcherServlet 初始化 Spring MVC 的九大核心组件
protected void initStrategies(ApplicationContext context) {
    this.initMultipartResolver(context);          // 1、初始化文件上传解析器
    this.initLocaleResolver(context);             // 2、初始化国际化解析器
    this.initThemeResolver(context);              // 3、初始化主题解析器
    this.initHandlerMappings(context);            // 4、初始化处理器映射器(HandlerMapping)
    this.initHandlerAdapters(context);            // 5、初始化处理器适配器(HandlerAdapter)
    this.initHandlerExceptionResolvers(context); // 6、初始化异常处理解析器
    this.initRequestToViewNameTranslator(context);// 7、初始化请求视图名称转换器
    this.initViewResolvers(context);              // 8、初始化视图解析器
    this.initFlashMapManager(context);             // 9、初始化 FlashMap 管理器,处理重定向传参
}

以 this.initHandlerMappings(context) 为例,进一步看一下初始化处理器映射器的细节:

// 定义 List 容器存储 HandlerMapping 实例
private List<HandlerMapping> handlerMappings;

// 初始化 HandlerMapping 的方法
private void initHandlerMappings(ApplicationContext context) {
    // 初始化集合为 null
    this.handlerMappings = null;

    // detectAllHandlerMappings 默认为 true,表示是否从所有容器(包括父子容器)检测 HandlerMapping
    if (this.detectAllHandlerMappings) {
        // 从 Spring 容器中查找所有 HandlerMapping 类型的 Bean,包括父容器
        Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context, HandlerMapping.class, true, false);

        // 如果找到的 HandlerMapping 不为空,则将它们加入 handlerMappings 集合
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());

            // 按照 @Order 注解或者 Ordered 接口进行排序,保证调用顺序
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }

        // 如果没有从容器中获取到 HandlerMapping,handlerMappings 依然为 null,则加载默认的 HandlerMapping
        if (this.handlerMappings == null) {
            // 通过配置文件 DispatcherServlet.properties 加载默认策略
            this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
        }
    }
}

初始化后,映射信息就已经被封装到HandlerMapping中了,可以在获取matchingBeans处打断点验证

image-20250729010443541

# 2、前端控制器执行主流程

上面讲解了一下,当服务器启动时,DispatcherServlet 会执行初始化操作,接下来,每次访问都会执行service方法,我们先宏观的看一下执行流程,在去研究源码和组件执行细节

image-20250729010534614

FrameworkServlet 复写了service(HttpServletRequest request, HttpServletResponse response) 、doGet(HttpServletRequest request, HttpServletResponse response)、doPost(HttpServletRequestrequest, HttpServletResponse response)等方法,这些方法都会调用processRequest方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response){
   this.doService(request, response);
}

进一步调用了doService方法,该方法内部又调用了doDispatch方法,而SpringMVC 主流程最核心的方法就是 doDispatch 方法

protected void doService(HttpServletRequest request, HttpServletResponse response) {
   this.doDispatch(request, response);
}

doDispatch方法源码

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null; // 处理器执行链对象,包含处理器和拦截器列表
    ModelAndView mv = null; // 模型视图对象

    // 1. 通过 HandlerMapping 匹配请求对应的处理器及拦截器,返回 HandlerExecutionChain
    mappedHandler = this.getHandler(processedRequest);

    // 2. 根据处理器类型,获取对应的 HandlerAdapter 适配器,用于执行处理器逻辑
    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

    // 3. 执行拦截器链中所有拦截器的 preHandle 方法,若返回 false 则中断后续处理
    mappedHandler.applyPreHandle(processedRequest, response);

    // 4. 使用处理器适配器执行具体的控制器 Handler 方法,得到 ModelAndView 对象
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    // 5. 执行拦截器链中所有拦截器的 postHandle 方法,可对模型视图做进一步处理
    mappedHandler.applyPostHandle(processedRequest, response, mv);

    // 6. 视图渲染及后续处理,将 ModelAndView 渲染成页面返回给客户端
    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception) dispatchException);
}

通过RequestMappingHandlerMapping 和RequestMappingHandlerAdaptor模拟主流程

上次更新: 2025/07/28, 17:18:00
SpringMVC的全注解开发
SpringMVC的异常处理机制

← SpringMVC的全注解开发 SpringMVC的异常处理机制→

Theme by Vdoing | Copyright © 2022-2025 Salmon's Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式