专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Spring MVC原理解析,Spring MVC的原理解析是什么?

一个简单的http请求(如下),从客户端发起请求,到服务端的对应的具体接口这之前发生了什么,然后服务端又是如何将请求结果返回到客户端上的?

http://gateway.dev.io/service/controller/interface                                      --Get

对于一次http的请求过程,无非就是客户端和服务端建立一次TCP连接,tomcat负责底层的Socket连接和请求报文数据解析,然后将请求的数据封装成request转交给Spring Mvc, Spring Mvc根据请求路径,Dispatch到这个路径绑定的处理函数去处理,并返回数据,再由tomcat输出给客户端。

大体的思路就是这样,对应关于Tomcat的部分更为详细的理解可以查看这篇文章

关于Spring Mvc的详细原理解析是今天的重点。

执行流程

65_1.png 执行流程图

上面是Spring MVC执行的整体流程。主要的重点有DispatchServlet, HandlerMapping, HandlerAdapter.

DispatchServlet

先来讲讲,tomcat是如何将封装好的请求转发到对应的request上的。

65_2.png 请求代码执行路径

上面的图,只需要看步骤1和步骤2和步骤3. hello.action的请求后缀为action,根据servlet-mapping的url-pattern映射到名为springmvc的DispatchServlet上,大体就是这么个流程。现在的Spring Boot默认配置 只有一个 DispatchServlet,对于静态资源后缀的请求,需要自己做额外的配置,可以参考这篇文章.

可以先来看一下DispatchServlet的继承图,从这里可以看出,DispatchServlet的本质就是Servlet, Servlet的生命周期中主要的方法是void init(ServletConfig),void service(ServletRequest req, ServletResponse res),void destory()

65_3.png DispatcherServlet类继承关系图

所以,我们可以找到DispatchServlet的doService方法,可以发现,doService()方法中最主要的代码就是doDispatch(HttpServletRequest request, HttpServletResponse response)方法。这个方法是分发请求的核心所在。

65_4.png doService()方法部分截图65_5.png doDispatch()方法部分截图 接下来,进入`doDispatch()`方法中,看看它做了什么,先来看看`checkMultiPart`这个方法是检查是否是二进制请求(文件上传请求)。如果是二进制请求,需要把request包装一层,返回`MutilpartHttpServletRequest`.

布尔变量multipartRequestParsed,判定在finally是否需要进行文件清理。

65_6.png 清理文件 下面主要的来了`mappedHandler = getHandler(processedRequest);` 65_7.png getHandler()方法 `getHandler()`方法获取的是`HandlerExecutionChain`,意思就是处理执行链。

那么这个被遍历的HandlerMapping到底是个什么玩意呢?

HandlerMapping

这个HandlerMapping其实在配置文件中已经有了配置,路径是 org\springframework\web\servlet\DispatcherServlet.properties 这个文件中。这里我们主要看的是RequestMappingHandlerMapping,这个其实就是平时代码中在Controller方法上写的@RequestMapping注解,通过这个注解将对应方法和请求地址的关联注入到Mapping中。

65_8.png DisaptchServlet配置文件65_9.png HandlerMapping的内容

从上图可以看出在RequestMappingHandlerMapping中,当前工程共有111个 url 与 对应的ControllerMethod存在映射关系。好了接下来,就可以根据requesturl从对应的HandlerMapping中获取到HandlerExecutionChain(其中包含对应的拦截器和对应的控制器)

65_10.png HandlerExecutionChain的内容 真正执行handle之前,有一系列操作,例如数据转换,格式化,数据验证这些都是需要由拦截器来做的。

另外需要注意的是,假如你自定义了n个拦截器,会发现HandlerExecutionChain会有n+2个拦截器,ConversionServiceExposingInterceptorResourceUrlProviderExposingInterceptor都属于默认的拦截器。ConversionServiceExposingInterceptor用于类型转换,ResourceUrlProviderExposingInterceptor用于提供资源的URL.而SessionInterceptor属于自定义的拦截器。

接着继续回doDispatch()方法。

65_11.png doDispatch()部分代码

接下来根据获取到的HandlerExecutionChain获取到对应的HandlerAdapter

HandlerAdapter

HandlerAdapter翻译过来是处理适配器,从Adapter中就应该察觉到这里用到了适配器模式。另外HandlerAdapter到底是用来干嘛的,我们不是已经到具体的HandleExecution,知道对应的处理方法了?

答案是mapping只是匹配controller,而执行controller还是需要HandlerAdapter,这里适配对应的处理器去执行核心处理逻辑方法invokeHandlerMethod

applyPreHandle

接下来,继续回到doDispacth()方法,这里会获取request的方法类型,判定是不是Get类型,get请求是有一个缓存。

接下来轮到applyPreHandle登场了,这里就前面提到的拦截器(Interceptor).

在这里我们可以回忆一下HandlerInterceptor的三个方法

public class MyInterceptor implements HandlerInterceptor {

    //表示控制器方法执行之前调用的方法,返回结果为boolean,如果为true,表示放行,如果为false,表示拦截
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("MyInterceptor.preHandle");
        return true;
    }

    //控制器执行完方法之后,视图结合之前
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");
    }

    //视图结合完成之后调用的方法
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}

applyPreHandle会遍历拦截器,执行每个拦截器对应的preHandle方法。


接下的流程

// Actually invoke the handler.
 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

终于,跋山涉水来到了具体的方法执行了,这里实际上对应的HandleAdapter执行具体的控制器方法。执行完毕后会返回一个ModelAndView对象,这个对象包含了用来存储处理完后的结果数据,以及显示该数据的视图。

    // 执行对应的postHandler方法
    mappedHandler.applyPostHandle(processedRequest, response, mv);

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
            throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

这里有个比较特殊的地方,HandlerInterceptor[]是倒着来执行对应的postHandler

接下来,继续执行

    // 处理ModelAndView
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
            @Nullable Exception exception) throws Exception {

        **********

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("No view rendering, null ModelAndView returned.");
            }
        }

        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            // Concurrent handling started during a forward
            return;
        }

        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

这里的重点是render(),这个方法决定究竟是转发还是重定向,或者说变成其他视图。

然后我们,又看到了熟悉的老伙计triggerAfterCompletion,又是执行拦截器视图结合完成后的方法,同样的他也是倒着来执行afterCompletion方法。

applyPreHandleapplyPostHandletriggerAfterCompletion、这三个方法可以得知拦截器的执行顺序,下面我用一张图来描述

65_12.png 拦截器方法执行顺序图 ## 总结

到这里,基本上整个Spring MVC流程就基本结束了。现在看这个图应该明白了许多。说白了就是根据url到HandlerMapping中获取到对应的HandlerExecutionChain, 然后再获取对应的HandlerAdapter去执行, 中间夹带了拦截器的方法,处理解析ModelAndView对象。

65_13.png

引用

本文主要参考简书用户肥朝的别怕,手把手带你撕、拉、扯下SpringMVC的外衣的文章

来源:https://juejin.im/post/5ecdc424e51d4578552704b2

文章永久链接:https://tech.souyunku.com/16756

未经允许不得转载:搜云库技术团队 » Spring MVC原理解析,Spring MVC的原理解析是什么?

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们