Spring MVC-DispatcherServlet 的源码解析

上篇文章把整个MVC的架构和核心流程介绍了一下,其中多次提到DispatcherServlet整个组件,我们知道整个流程看下来DispatcherServlet是一个核心组件,接收请求、分发请求给控制器进行处理。这个组件继承自FrameworkServletHttpServlet,然后与HandlerMapping,HandlerAdapter,ViewResolver等组件,完成了从请求接受到响应返回到完整流程。

如果你去看Spring Framework的参考文档,DispatcherServlet的主要职责是这样描述的:

  • 初始化Web应用程序上下文(WebApplicationContext)和核心组件
  • 根据请求路径和方法分派请求到控制器
  • 处理视图解析和渲染
  • 支持异常处理、国际化、文件上传等高级功能

1. DispatcherServlet 的继承关系与初始化过程

1.1 继承关系

我们可以看到,DispatcherServlet是直接继承FrameworkServlet

然后FrameworkServlet则是继承HttpServlet,实现了ApplicationContextAware接口,这里正好用这个来解释下extendsimplements的区别。

这里我们可以看到DispatcherServlet extends FrameworkServletBean,意思是:

  • 初始化参数注入(initServletBean())
  • 处理Servlet生命周期
  • 读取初始化参数到Spring的Bean属性里

implements ApplicationContextAware ,首先这个接口的作用是Spring容器在创建Bean时,把当前ApplicationContext(应用上下文)注入到Bean中。所以实现这个接口的目的是,感知容器实现自定义接口中的方法。

在这里我们可以看出来,extends是继承已有逻辑,implements是遵循外部规范。

1.2 初始化过程

DispatcherServlet 的初始化发生在 Servlet 容器(如 Tomcat)启动时,主要通过 init 方法触发,最终调用 onRefresh 方法完成组件初始化。以下是关键源码分析:

1.2.1 init方法(来自HttpSevletBean)

scala 复制代码
public abstract class HttpServletBean extends HttpServlet {
    @Override
    public void init() throws ServletException {
        // 设置 Servlet 配置参数
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
        bw.setPropertyValues(pvs, true);
        // 调用子类的初始化方法
        initServletBean();
    }
}

这个方法的作用就是将Servlet配置注入到DispatcherServlet

1.2.2 initServletBean

FrameworkServlet 重写了 initServletBean,初始化 Web 上下文:

scala 复制代码
public abstract class FrameworkServlet extends HttpServletBean {
    @Override
    protected void initServletBean() throws ServletException {
        try {
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        } catch (Exception ex) {
            throw new ServletException("Context initialization failed", ex);
        }
    }

    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        // 创建或获取 WebApplicationContext
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext) wac);
            }
        }
        if (wac == null) {
            wac = createWebApplicationContext(rootContext);
        }
        // 发布上下文到 ServletContext
        if (!this.refreshEventReceived) {
            onRefresh(wac);
        }
        return wac;
    }
}

这里主要是调用onRefresh触发子类的初始化逻辑,关键的点是webApplicationContext是Spring MVC的IoC容器,管理着MVC的所有相关Bean。

1.2.3 onRefresh方法

这个方法就是要初始化MVC的核心组件:

scss 复制代码
public class DispatcherServlet extends FrameworkServlet {
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
}

我们在上节讲到的核心组件都是在这里初始化的。

2. 请求分发核心逻辑:doDispatch方法

我们说过DispatcherServlet 还有一个核心职责是处理 HTTP 请求,其请求分发的核心方法是 doDispatch,位于 DispatcherServlet 类中。以下是 doDispatch 的源码分析:

ini 复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 检查是否为 multipart 请求
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 获取 HandlerExecutionChain
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // 获取 HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 处理 Last-Modified 头部(用于缓存)
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            // 执行拦截器的 preHandle 方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 调用 HandlerAdapter 处理请求
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 处理异步请求
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            // 设置默认视图名(如果未指定)
            applyDefaultViewName(processedRequest, mv);

            // 执行拦截器的 postHandle 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
            dispatchException = ex;
        } catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }

        // 处理结果(视图渲染或异常处理)
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
        }
    }
}

从这个源码能够看出来整个的核心步骤:

检查multipart --> 获取HandlerExceptionChain --> 获取HandlerAdapter --> 处理Last- Modified缓存 --> 执行拦截器preHandler --> 调用HandlerAdapter --> 执行拦截器postHandler --> 处理结果

整个源码的关键是:HandlerExecutionChain,主要是调用getHandler遍历所有HandlerMapping查找匹配的HandlerExecutionChain(包含Handler和拦截器)。

asyncManager.isConcurrentHandlingStarted()支持异步请求。

3. 总结

这篇文章主要就是说了DispatcherServlet,DispatcherServlet 是 Spring MVC 的核心调度器,通过初始化九大组件和 doDispatch 方法,协调请求的映射、处理和视图渲染。另外,也可以支持异步处理和异常处理

相关推荐
字节源流2 小时前
关于maven的依赖下不下来的问题
java·maven
pjx9873 小时前
服务间的“握手”:OpenFeign声明式调用与客户端负载均衡
java·运维·spring·负载均衡
prinrf('千寻)3 小时前
MyBatis-Plus 的 updateById 方法不更新 null 值属性的问题
java·开发语言·mybatis
老华带你飞3 小时前
实习记录小程序|基于SSM+Vue的实习记录小程序设计与实现(源码+数据库+文档)
java·数据库·spring boot·小程序·论文·毕设·实习记录小程序
在未来等你4 小时前
互联网大厂Java求职面试:AI与大模型应用集成及云原生挑战
java·微服务·ai·kubernetes·大模型·embedding·spring ai
源码技术栈4 小时前
SaaS基于云计算、大数据的Java云HIS平台信息化系统源码
java·大数据·云计算·云his·his系统·云医院·区域his
编程、小哥哥4 小时前
互联网大厂Java面试:从Spring Boot到微服务架构的技术深挖
java·spring boot·redis·微服务·prometheus·面试技巧
揽你·入怀4 小时前
数据结构:ArrayList简单实现与常见操作实例详解
java·开发语言
okok__TXF5 小时前
SpringBoot3+AI
java·人工智能·spring
冬瓜的编程笔记5 小时前
【八股战神篇】MySQL高频面试题
数据库·mysql·面试