SpringMVC的执行流程与源码分析

这篇具有很好参考价值的文章主要介绍了SpringMVC的执行流程与源码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

通过深入分析Spring MVC的源码,我们可以更好地理解其工作原理和内部机制。这有助于我们更好地使用该框架进行Web应用程序的开发,并解决实际开发中遇到的问题。同时,对于学习和研究Spring MVC框架的人来说,阅读源码并进行分析也是一种重要的学习和提升手段。

SpringMVC概述

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。是目前主流的实现MVC设计模式的框架,提供前端路由映射、视图解析等功能.

MVC模式

MVC是Model-View-Controller的缩写,是一种常用的软件设计模式。在这种设计模式中,模型层(Model)负责处理业务逻辑和数据,视图层(View)负责呈现用户界面,而控制器层(Controller)负责处理用户交互。

MVC把三层架构中的UI层再度进行了分化,分成了控制器、视图、实体三个部分,控制器完成页面逻辑,通过实体来与界面层完成通话;而Controller层直接与三层架构中的业务逻辑层进行对话。三层架构和MVC可以共存。 三层是基于业务逻辑来分的,而MVC是基于页面来分的。

特点:

 MVC重要特点就是两种分离:

  视图和数据模型的分离:使用不同的视图对相同的数据进行展示;分离可视和不可视的组件,能够对模型进行独立测试。因为分离了可视组件减少了外部依赖利于测试。(数据库也是一种外部组件)

  视图和表现逻辑(Controller)的分离:Controller是一个表现逻辑的组件,并非一个业务逻辑组件。MVC可以作为表现模式也可以作为建构模式,意味这Controller也可以是业务逻辑。分离逻辑和具体展示,能够对逻辑进行独立测试。

  优点:耦合性低;重用性高;生命周期成本低;部署块;可维护性高;有利软件工程化管理。

MVC框架详解可以看这位大佬的博文https://zhuanlan.zhihu.com/p/417635345

SpringMVC的主要组件

DispatcherServlet 前端控制器

前端控制器(也称中央控制器),是整体流程控制的中心,由其调用其它组件处理用户的请求,有效的降低了组件间的耦合性

HandlerMapping 处理器映射器

处理器映射器,负责根据用户请求找到对应具体的Handler处理器和拦截器,并将结果组装成一个HandlerExecutionChain返回

HandlerAdapter 处理器适配器

处理器适配器,通过它对处理器进行执行,基于适配器模式开发,如果使用@RequestMapping注解标识方法,那么执行的就是方法对象

如果处理器类上或方法上使用了@ResponseBody注解,那么就没有以下两步

视图解析器(ViewResolver)

视图解析器,将处理结果生成View视图,给方法返回的视图地址解析添加前后缀,返回完整的视图地址

视图(View)

视图,最终产出结果,常用视图如jsp、html ,视图最终渲染由前端控制器完成

SpringMVC执行流程

前端控制器拦截请求

执行doService方法

首先,当一个请求进入controller前会先被DispatcherServlet拦截,进入doService方法,下面是整个doService方法的代码:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.logRequest(request);
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration<?> attrNames = request.getAttributeNames();

            label95:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label95;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }

        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }

            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        try {
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }
this.logRequest()方法

进入doService方法后,首先执行this.logRequest方法,记录请求的详细信息,包括请求的 URL、参数、头信息等。这些信息通常会被记录到日志文件中,以便于后续的分析和调试。

  

attributesSnapshot属性

attributesSnapshot属性用于存储请求的属性快照。它的作用是在请求处理过程中,捕获请求的属性状态,以便在请求处理结束后进行清理或恢复。

SpringMVC的执行流程与源码分析,java,后端

在HttpServletRequest对象中设置属性:
  • 将Web应用程序上下文对象设置为HttpServletRequest的属性。这允许在请求处理过程中访问应用程序上下文中的bean和其他资源。
 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
  • 将区域解析器(LocaleResolver)设置为HttpServletRequest的属性。区域解析器用于解析客户端的区域设置信息,以便在应用程序中进行国际化和本地化。
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  • 将主题解析器(ThemeResolver)设置为HttpServletRequest的属性。主题解析器用于解析客户端的主题信息,以便在应用程序中应用不同的主题样式。
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  • 将主题资源(ThemeSource)设置为HttpServletRequest的属性。主题资源用于提供主题相关的资源,如样式表、图片等。
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
检查flashMapManager是否为非空。

SpringMVC的执行流程与源码分析,java,后端

  • 如果不为空,表示存在FlashMap管理器。
  • 然后,它使用flashMapManager从请求和响应中检索并更新FlashMap。FlashMap是一种用于在重定向请求之间传递属性的数据结构。
 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  •  如果成功检索到输入的FlashMap,则将其设置为请求的属性INPUT_FLASH_MAP_ATTRIBUTE,并使用Collections.unmodifiableMap使其不可修改。
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  • 接着,它创建一个新的空FlashMap,并将其设置为请求的属性OUTPUT_FLASH_MAP_ATTRIBUTE。
  • 最后,它将flashMapManager设置为请求的属性FLASH_MAP_MANAGER_ATTRIBUTE。
  request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

FlashMapManager是Spring MVC中用于管理FlashMap的接口。FlashMap用于在重定向请求之间传递属性,通常用于在重定向后显示一次性消息或数据。FlashMapManager负责在请求之间存储和检索FlashMap,并在适当的时候清除FlashMap。具体实现可以根据需要选择,Spring提供了默认的实现,也可以自定义实现以满足特定需求。 

执行doDispatch方法

下面是doDispatch方法的所有代码

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

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

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    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;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

doDispatch方法逐行解析:

(1)将当前的HttpServletRequest对象赋值给processedRequest变量,这样可以在后续的代码中使用processedRequest来代表原始的请求对象。

(2)声明一个HandlerExecutionChain类型的变量mappedHandler,并将其初始化为null。这个变量用于存储处理请求的处理器(handler)以及相关的拦截器链。

(3)声明一个boolean类型的变量multipartRequestParsed,并将其初始化为false。这个变量通常用于跟踪是否已经解析了multipart请求。

(4)通过WebAsyncUtils工具类获取与当前请求相关的WebAsyncManager对象,用于处理异步请求的管理。

SpringMVC的执行流程与源码分析,java,后端

(5)声明一个类型为ModelAndView的变量mv,并将其初始化为null。ModelAndView是Spring MVC中用于将模型数据和视图名称封装在一起的类。

(6)声明一个类型为Exception的变量dispatchException,并将其初始化为null。这个变量通常用于存储在请求分派过程中发生的异常。

SpringMVC的执行流程与源码分析,java,后端

(7)调用checkMultipart方法对请求进行处理

检查是否为multipart请求或对multipart请求进行解析。处理后的请求对象被赋值给processedRequest变量。

processedRequest = this.checkMultipart(request);

检查处理后的请求对象是否与原始请求对象相同

  • 如果不同,则说明multipart请求已经被解析。
 multipartRequestParsed = processedRequest != request;
  • 如果相同就会执行catch语句抛出异常

SpringMVC的执行流程与源码分析,java,后端

(8)调用getHandler获取处理器执行链

SpringMVC的执行流程与源码分析,java,后端

(9.1)处理器为空报404找不到处理器异常直接返回,返回前会执行finally中的代码

  • 检查是否异步处理已经开始,即asyncManager.isConcurrentHandlingStarted()返回true。
  • 如果处理程序不为null,则调用处理程序的applyAfterConcurrentHandlingStarted方法,传入处理后的请求和响应对象,以便在并发处理开始后执行相关操作。
  • 如果异步处理未开始,它会检查是否multipart请求已经被解析,即multipartRequestParsed为true。
  • 如果是,它调用cleanupMultipart方法对处理后的请求进行清理,通常用于清理multipart请求相关的资源。

SpringMVC的执行流程与源码分析,java,后端

(9.2)处理器不为空,调用getHandlerAdapter()

SpringMVC的执行流程与源码分析,java,后端getHandlerAdapter方法源码:

调用supports()方法

判断是够支持给定的处理器,如果找到一个适配器和处理器匹配,将返回这个适配器。

  protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }

        throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
(10)处理器执行链调用applyPreHandle()执行处理器执行链的前置拦截器

SpringMVC的执行流程与源码分析,java,后端

applyPreHandle方法源码:

 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HandlerInterceptor[] interceptors = this.getInterceptors();
// 获取拦截器数组

if (!ObjectUtils.isEmpty(interceptors)) {
    // 如果拦截器数组不为空
    for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
        // 遍历拦截器数组
        HandlerInterceptor interceptor = interceptors[i];
        // 获取当前拦截器

        if (!interceptor.preHandle(request, response, this.handler)) {
            // 如果拦截器的preHandle方法返回false
            this.triggerAfterCompletion(request, response, (Exception)null);
            // 触发afterCompletion方法
            return false;
            // 返回false
        }
    }
}
        return true;
    }
(11)处理器适配器调用handle()方法

将处理器执行链(包括拦截器与处理器)交给适配器,适配器调用handle方法执行处理器执行链以及

SpringMVC的执行流程与源码分析,java,后端

(12)处理器执行链调用applyPostHandle()执行处理器执行链的后置拦截器

SpringMVC的执行流程与源码分析,java,后端

(13)在没有出现异常的情况下会执行以下代码:

该方法的作用:处理调度结果。它首先检查是否有异常,如果有异常则处理异常;然后根据处理结果进行渲染;最后触发请求完成后的处理。

 this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
执行doService中的finally代码块
  1.  首先,它使用WebAsyncUtils.getAsyncManager(request)获取请求的异步管理器,并检查是否并发处理已经开始,即isConcurrentHandlingStarted()返回false。
  2.  然后,它检查attributesSnapshot是否不为null。
  3.  如果上述条件都满足,它调用restoreAttributesAfterInclude方法,将之前保存的请求属性恢复到请求中。这通常用于在包含请求处理过程中恢复之前保存的请求属性。

SpringMVC的执行流程与源码分析,java,后端

参考文章

超详细的!!!MVC架构模式说明 - 一只码农-小俊的文章 - 知乎
https://zhuanlan.zhihu.com/p/417635345
文章来源地址https://www.toymoban.com/news/detail-813168.html

到了这里,关于SpringMVC的执行流程与源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Flink window 源码分析1:窗口整体执行流程

    注:本文源码为flink 1.18.0版本。 其他相关文章: Flink window 源码分析1:窗口整体执行流程 Flink window 源码分析2:Window 的主要组件 Flink window 源码分析3:WindowOperator Flink window 源码分析4:WindowState Window 本质上就是借助状态后端缓存着一定时间段内的数据,然后在达到某些条件

    2024年01月16日
    浏览(34)
  • 【Android12】Monkey压力测试源码执行流程分析

    Monkey是Android提供的用于应用程序自动化测试、压力测试的测试工具。 其源码路径(Android12)位于 部署形式为Java Binary 通过Monkey,可以模拟用户的Touch(单指、多指、手势)、按键(key)事件等,检测应用程序发生的ANR、Crash事件,并收集相关Debug信息等。 例如测试应用com.packa

    2024年03月22日
    浏览(29)
  • 【源码分析】XXL-JOB的执行器的注册流程

    目的:分析xxl-job执行器的注册过程 流程: 获取执行器中所有被注解( @xxlJjob )修饰的 handler 执行器注册过程 执行器中任务执行过程 版本: xxl-job 2.3.1 建议:下载 xxl-job 源码,按流程图 debug 调试, 看堆栈信息并按文章内容理解执行流程 。 完整流程图: 部分流程图: 首先启

    2023年04月22日
    浏览(29)
  • 【SpringMVC】| SpringMVC执行流程原理 | 常用注解 剥析

    MVC全称Model View Controller,是一种设计创建Web应用程序的模式。这三个单词分别代表Web应用程序的三个部分: Model (模型):指数据模型。用于存储数据以及处理用户请求的业务逻辑。在Web应用中,JavaBean对象,业务模型等都属于Model。 View (视图):用于展示模型中的数据的

    2024年02月06日
    浏览(25)
  • SpringMVC的执行流程

    过去的开发中,视图阶段(老旧JSP等) 1.首先用户发送请求到前端控制器DispatcherServlet(这是一个调度中心) 2.前端控制器DispatcherServlet收到请求后调用处理器映射器HandlerMapping 3.处理器映射器HandlerMapping找到具体的处理器,可查找xml配置或注解配置,生成处理器对象及处理器拦截器

    2024年02月19日
    浏览(25)
  • SpringMVC_执行流程

    DispatcherServlet :前端控制器,用于对请求和响应进行统一处理 HandlerMapping :处理器映射器,根据 url/method可以去找到具体的 Handler(Controller) Handler :具体处理器(程序员,以后开发这一部分需要) HandlerAdapter :处理器适配器,进行处理器方法的执行 ViewResolver :处理视图相关的

    2024年02月09日
    浏览(26)
  • SpringMVC 执行流程

    SpringMVC 框架 ​ SpringMVC 是一个基于 Java 的实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架,通过把 Model,View,Controller 分离,将 Web 层进行职责解耦,把复杂的 Web 应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。 简而言之,SpringMVC 就

    2024年03月25日
    浏览(100)
  • springMVC执行流程详解

    springMVC执行流程 主要由model层,view层和controller层组成。 1.1,jsp模型 主要是结构简单,开发这个小型项目的效率高,主要是由这个jsp和javaBean组成。但是jsp同时负责了controller层和view层,因此所有的代码都写在这个jsp里面,导致这个代码的重用性很低,维护不方便,因此这种

    2024年02月16日
    浏览(28)
  • 第12章:SpringMVC执行流程

    1.SpringMVC 常用组件 ①、DispatcherServlet: 前端控制器 ,框架提供。 作用:统一处理请求和响应,整个流程的控制中心,由它调用其他组件处理用户的请求 ②、HandlerMapping: 处理器映射器 ,框架提供 作用:根据请求的url、method等信息查找Handler即控制器方法 ③、Handler: 处理器,

    2024年02月08日
    浏览(32)
  • 15、SpringMVC之常用组件及执行流程

    DispatcherServlet 是前端控制器,由框架提供,不需要工程师开发; 作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求。 HandlerMapping 是处理器映射器,由框架提供,不需要工程师开发; 作用:根据请求的 url、method 等信息去查找 Handler(即控制

    2024年02月05日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包