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

相关推荐
用户35218024547521 小时前
当 Prompt 学会"热更新":Spring Boot × Nacos3 AI 实战
java·spring boot·ai编程
Darling噜啦啦1 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
swipe1 天前
正则表达式入门到进阶:从表单校验到手写模板引擎
前端·javascript·面试
神奇小汤圆1 天前
RAG大厂面试题汇总:向量检索、混合检索、Rerank、幻觉处理高频问题
面试
东坡白菜1 天前
破局全栈:一个前端开发的Java入门实战记录(1)
java·全栈
唐青枫1 天前
Java Tomcat 实战指南:从 Servlet 容器到 Spring Boot 部署
java
wsaaaqqq1 天前
roudan:自由选择实体、灵活操作数据、快速写入数据库的 Java 框架
java
假如让我当三天老蒯1 天前
回归基本功:Map/Set 与 WeakMap/WeakSet 的区别
前端·面试
plainGeekDev1 天前
null 判断 → Kotlin 可空类型
android·java·kotlin
糖拌西瓜皮1 天前
Java开发者视角:深入理解Node.js异步编程模型
java·后端·node.js