原理 - Spring:(二)DispatcherServlet.doDispatch方法流程

DispatcherServlet工作原理
本文深入解析了DispatcherServlet的doDispatch方法,阐述了Spring MVC中请求处理的完整流程,包括Handler的查找、调用及拦截器的工作机制。

该文章主要介绍了DispatcherServlet的主要逻辑实现 – doDispatch方法,包括拦截器各个方法调用时机、Handler的查找和调用等主要逻辑

DispatcherServlet主要在doDispatch中分发请求以及拦截器的调用,
(源代码下方有流程图)

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    ...
      // 检查是否为multipart/*类型
    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request);

    
    if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
    }

    // 确认支持当前请求Handler的Adapter,用于调用Handler的handle方法
    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代理执行Handler的handle方法
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

    ...

    //进行视图的渲染
    applyDefaultViewName(processedRequest, mv);
    
    //调用拦截器链的postHandle方法
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    ...
}

转换成流程图如下:
在这里插入图片描述
当一个请求到来时:
1、根据request的uri和其他信息(比如HttpMethod、Header等信息)确定Handler,选择支持该类型Hanlder的HandlerAdapter(HandlerAdapter主要用于对Handler的handle方法调用:处理函数参数的注入、返回值的处理等);

    //确认处理当前请求的Handler
    mappedHandler = getHandler(processedRequest);

    ...

    // 确认支持当前请求Handler的Adapter,用于调用Handler的handle方法
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

2、如果是Get或者Head请求,判断文件修改时间;如果未修改直接返回;RequestMappingAdapter的修改时间默认返回-1,即判断文件修改时间总是返回false;

    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;
        }
    }

3、从步骤1中确定的Hanlder是一条包含拦截器集合和Hanlder的调用链,DispatcherServlet调用所有拦截器的preHandle方法;

    // 对拦截器链preHandle的调用
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }

4、handler通过adapter进行调用,主要进行参数值的匹配和注入

    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

5、渲染视图

    //进行视图的渲染
    applyDefaultViewName(processedRequest, mv);

6、调用拦截器postHandle方法(逆序调用)

    //调用拦截器链的postHandle方法
    mappedHandler.applyPostHandle(processedRequest, response, mv);

7、对正常完成的部分拦截器调用afterCompletion;例如

拦截器1 --> 拦截器2 --> 拦截器3 …
拦截器3抛出异常
afterCompletion的调用过程
拦截器2 --> 拦截器1 …

这里拦截器调用有三种情况:
1、对于拦截器返回false的情况,直接返回,不调用postHandle和afterCompletion方法;
2、对于中间拦截器抛出异常,对于成功执行的拦截器,反序调用其afterCompletion方法;
3、如果拦截器成功执行,所有拦截器的afterCompletion反序调用
即按照成功执行preHandle方法后的拦截器逆序调用。

Circular view path [test]: would dispatch back to the current handler URL [/test] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.) at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:210) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:148) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:314) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1438) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1168) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1106) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.2.10.jar:6.2.10] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.2.10.jar:6.2.10] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.44.jar:6.0] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.2.10.jar:6.2.10]
09-13
2025-06-24T10:38:45.229 08:00 ERROR 10592 --- [exam] [nio-8088-exec-9] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-8088-exec-9] Exception processing template "student/doExam": An error happened during template parsing (template: "class path resource [templates/student/doExam.html]") org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/student/doExam.html]") at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241) ~[thymeleaf-3.1.3.RELEASE.jar:3.1.3.RELEASE] at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100) ~[thymeleaf-3.1.3.RELEASE.jar:3.1.3.RELEASE] at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666) ~[thymeleaf-3.1.3.RELEASE.jar:3.1.3.RELEASE] at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1103) ~[thymeleaf-3.1.3.RELEASE.jar:3.1.3.RELEASE] at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1077) ~[thymeleaf-3.1.3.RELEASE.jar:3.1.3.RELEASE] at org.thymeleaf.spring6.view.ThymeleafView.renderFragment(ThymeleafView.java:372) ~[thymeleaf-spring6-3.1.3.RELEASE.jar:3.1.3.RELEASE] at org.thymeleaf.spring6.view.ThymeleafView.render(ThymeleafView.java:192) ~[thymeleaf-spring6-3.1.3.RELEASE.jar:3.1.3.RELEASE] at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1435) ~[spring-webmvc-6.2.3.jar:6.2.3] at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1167) ~[spring-webmvc-6.2.3.jar:6.2.3] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1105) ~[spring-webmvc-6.2.3.jar:6.2.3] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:978) ~[spring-webmvc-6.2.3.jar:6.2.3] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkSe
06-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子的木木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值