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 方法,协调请求的映射、处理和视图渲染。另外,也可以支持异步处理和异常处理

相关推荐
cat三三13 分钟前
java之异常
java·开发语言
T___T14 分钟前
一个定时器,理清 JavaScript 里的 this
前端·javascript·面试
浙江第二深情18 分钟前
前端性能优化终极指南
java·maven
养乐多072234 分钟前
【Java】IO流
java
俊男无期34 分钟前
超效率工作法
java·前端·数据库
LYFlied34 分钟前
【每日算法】LeetCode 46. 全排列
前端·算法·leetcode·面试·职场和发展
中国胖子风清扬38 分钟前
SpringAI和 Langchain4j等 AI 框架之间的差异和开发经验
java·数据库·人工智能·spring boot·spring cloud·ai·langchain
月明长歌1 小时前
【码道初阶】牛客TSINGK110:二叉树遍历(较难)如何根据“扩展先序遍历”构建二叉树?
java·数据结构·算法
用户2190326527351 小时前
Spring Boot + Redis 注解极简教程:5分钟搞定CRUD操作
java·后端
Alice1 小时前
linux scripts
java·linux·服务器