Spring MVC的原理

Spring MVC中的MVC即模型-视图-控制器,该框架围绕一个DispatcherServlet设计而成,DispatcherServlet会把请求分发给各个处理器,并支持可配置的处理器映射和视图渲染等功能。Spring MVC的具体工作流程如下:
(1)客户端发起HTTP请求:客户端将请求提交到DispatcherServlet
(2)寻找处理器:由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理该请求的Controller
(3)调用处理器:DispatcherServlet将请求提交到Controller
(4)调用业务处理逻辑并返回结果;Controller在调用业务处理逻辑后,返回ModelAndView
(5)处理视图映射并返回模型:DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
(6)HTTP响应:视图负责将结果在客户端浏览器上渲染和展示。
在这里插入图片描述核心代码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 {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request./*mappedHandler:调用链包含handler、interceptorList、interceptorIndexhandler:浏览器发送的请求所匹配的控制器方法interceptorList:处理控制器方法的所有拦截器集合interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行*/mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.// 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.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;}// Actually invoke the handler.// 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象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) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}// 后续处理:处理模型数据和渲染视图processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}
}

HandlerMapping介绍

HandlerMapping在这个SpringMVC体系结构中有着举足轻重的地位,充当着url和Controller之间映射关系配置的角色。HandlerMapping是接口,Spring MVC提供了一系列HandlerMapping的实现,根据一定的规则选择controller。如果当前的HandlerMappign实现中没有能够满足你所需要的规则是,可以通过实现HandlerMapping接口进行扩展。它主要有三部分组成:HandlerMapping映射注册、根据url获取对应的处理器、拦截器注册。
总结
HandlerMapping是处理器映射器,根据请求找到处理器Handler,但并不是简单的返回处理器,而是将处理器和拦截器封装,形成一个处理器执行链(HandlerExecuteChain)。
核心伪代码如下:
getHandler伪代码

 1 // 获取处理器执行链对象2 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {3    // SpringMVC默认实现三种类型的HandlerMapping: 4    // RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping5    if (this.handlerMappings != null) {6       // 遍历MVC容器中的处理器映射器7       for (HandlerMapping mapping : this.handlerMappings) {8          // 获取请求对应的处理器执行链9          HandlerExecutionChain handler = mapping.getHandler(request);
10          // 处理器执行链不为空
11          if (handler != null) { 
12             // 返回匹配到的处理器执行链 
13             return handler;  
14          }
15       }
16    }
17    // 返回null
18    return null;  
19 }

getHandler获取执行器链

1 // 为请求获取handler处理器,若没有Handler处理器,获取mvc默认的handler处理器2 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {3    // 获得处理器(HandlerMethod或者HandlerExecutionChain),该方法是抽象方法,由子类实现4    Object handler = getHandlerInternal(request);5    // 获得不到,则使用默认处理器6    if (handler == null) {7       handler = getDefaultHandler();8    }9    // 获得不到,则返回 null
10    if (handler == null) {
11       return null;
12    }
13    
14    // 如果找到的处理器是String类型,则从Spring容器中找到对应的Bean作为处理器
15    if (handler instanceof String) {
16       String handlerName = (String) handler;
17       handler = obtainApplicationContext().getBean(handlerName);
18    }
19 
20    // 创建HandlerExecutionChain对象(包含处理器和拦截器)
21    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
22 
23    // 跨域请求的处理
24    if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
25       CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
26       CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
27       config = (config != null ? config.combine(handlerConfig) : handlerConfig);
28       executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
29    }
30 
31    return executionChain;
32 }

getHandlerExectuionChain 构建执行器链

 1 //  为handler处理器构建HandlerExecutionChain对象,包含应用拦截器2 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {3    // 创建 HandlerExecutionChain 对象4    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?5          (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));6 7    // 获得请求路径8    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);9    // 遍历 adaptedInterceptors 数组,获得请求匹配的拦截器
10    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
11       // 需要匹配,若路径匹配,则添加到 chain 中
12       if (interceptor instanceof MappedInterceptor) {
13          MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
14          if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
15             chain.addInterceptor(mappedInterceptor.getInterceptor());
16          }
17       }
18       // 无需匹配,直接添加到 chain 中
19       else {
20          chain.addInterceptor(interceptor);
21       }
22    }
23    return chain;
24 }

相关问题

1.为什么不直接调用对应handler处理请求,而是先找到适配器
提到适配器,就知道它应用了适配器模式。
为什么要用适配器模式呢?
因为控制器接受请求的方式不同,即实现Controller的方式不同。有三种实现方式,一种是实现Controller接口方式,一种是用@Controller注解的方式,还有一种是直接接受的Http请求。
不同的Controller的实现方式,如果用if…else判断去处理的话,就会违背开放封闭原则 。

if(controller typeof  @Controller){处理@Controller注解逻辑
}
if(controller typeof ControllerInterface){处理实现controller接口的逻辑
}
if(controller typeof http){处理实现http请求类型的逻辑
}

因为多加一种controller实现方式就得多一个判断。因此SpringMVC就引入了适配器模式
而HandlerAdapter组件具有接口转换的能力,可以统一使用该接口来处理不同的controller的实现。
方便controller的扩展。
这里就是把变化的controller上面建立一层统一处理层,封装、屏蔽底层的controller实现方式的变化。
在这里插入图片描述

2.HandlerExecutionChain使用了什么设计模式,结合源码分析
责任链模式属于三种设计模式分类中行为型模式,责任链模式为请求对象创建一个接收者处理链,通常接收者中会存在下一个接收者的引用,如果当前接收者无法处理当前请求时,则将请求对象传递到下一个接收者进行处理,从而实现责任链,直到可以处理请求对象为止。责任链模式中请求发送者无需知道是哪个接收者处理的请求对象以及请求对象处理的具体过程,能够很好的将请求发送者和接收者进行解耦。责任链模式通常用于各种过滤器链、拦截处理链中,例如JavaWeb中的Filter过滤器等等。Spring框架中责任链模式的应用有很多,包括web请求设计,security安全权限设计,aop切面设计等等。

SpringMVC中实际上是基于Servlet的框架,当客户端发送请求到web时,都会进入DispatcherServlet中,然后根据Servlet的生命周期去执行doService方法,在doService方法中有一个关键方法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 {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);//获取处理对象mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(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;}applyDefaultViewName(processedRequest, mv);//责任链模式实现二:执行调用链的后置处理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);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

HandlerExecutionChain的责任链模式,将需要执行的HandlerInterceptor拦截器,添加在HandlerExecutionChain责任链的interceptorList集合中,然后依次执行HandlerInterceptor相应的处理方法。以preHandle方法为例,在处理方法中,根据返回的处理boolean判断是否要继续进行下面的HandlerInterceptor对象的处理,最终完成整个调用链的拦截处理。HandlerExecutionChain的实现实际上是责任链模式的一种变性用法,它减少了不同处理对象的引用依赖,采用集合的方式来存放处理链对象,降低了耦合度

	//HandlerExecutionChain的applyPreHandle方法//责任链模式,将处理对象给接收者,并不关心内部如何处理与调用boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);//调用拦截器的preHandle方法,执行所有拦截器的preHandle处理方法if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;}

参考:http://www.manongjc.com/detail/41-uiajoseglafvxgf.html
https://blog.csdn.net/shang_0122/article/details/119335479
转载:https://blog.csdn.net/weixin_41605937/article/details/133376446

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/637605.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务不死 — 共享变量在策略引擎项目的落地详解

01 背景 1、共享变量的提出 前段时间&#xff0c;来自亚马逊 Prime Video 团队的一个案例研究在开发者社区中掀起了轩然大波。大体是这样一件事&#xff0c;作为一个流媒体平台&#xff0c;Prime Video每天都会向客户提供成千上万的直播流。为了确保客户无缝接收内容&#xff0…

【动态规划】【数学】【C++算法】805 数组的均值分割

作者推荐 【动态规划】【数学】【C算法】18赛车 本文涉及知识点 动态规划 数学 805 数组的均值分割 给定你一个整数数组 nums 我们要将 nums 数组中的每个元素移动到 A 数组 或者 B 数组中&#xff0c;使得 A 数组和 B 数组不为空&#xff0c;并且 average(A) average(B)…

Java基础(2)

一 String、StringBuffer、StringBuilder String 为什么要设计成不可变的&#xff1f; String是不可变的&#xff08;修改String时&#xff0c;不会在原有的内存地址修改&#xff0c;而是重新指向一个新对象&#xff09;&#xff0c;String用final修饰&#xff0c;不可继承&…

两个bbox的IoU计算步骤分析

IoU&#xff1a;交并比&#xff0c;数值上等于交集面积除以并集面积。 两个bbox的位置关系无外乎以上三种情况&#xff1a;&#xff08;1&#xff09;部分相交。&#xff08;2&#xff09;不相交。&#xff08;3&#xff09;包含。 计算步骤&#xff1a; 计算交集&#xff08…

操作系统-操作系统引导(磁盘 操作系统引导过程)

文章目录 总览一个刚买来的磁盘&#xff08;硬盘&#xff09;往磁盘安装操作系统后操作系统引导过程例&#xff1a;windows操作系统的初始化程序 总览 一个刚买来的磁盘&#xff08;硬盘&#xff09; 此时空空如也 往磁盘安装操作系统后 操作系统在C盘 主引导记录不属于某…

java idea 中的 Scratches and Consoles

IDEA 中&#xff0c;"Scratches and Consoles" 是一个用于临时代码编辑和交互式开发的工具窗口&#xff0c;作用如下&#xff1a;Scratches&#xff08;草稿&#xff09;&#xff1a;Scratches 是一个用于临时编写和运行代码片段的工具&#xff0c;你可以在其中创建临…

Python基础第四篇(Python函数)

文章目录 一、函数介绍二、函数的定义三、函数的参数与返回值四、函数说明文档五、函数的嵌套六、变量域七、函数案例1.源代码2.读出结果 在程序设计领域&#xff0c;函数成为一个不可或缺的角色&#xff0c;它们为我们提供了精练、高效和易于管理的编程方式。本篇博客将带您深…

无人机航迹规划(四):七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划(提供MATLAB代码)

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁殖…

【JavaEE】_网络编程基础

目录 1. 网络编程基础 1.1 网络编程定义 1.2 网络编程中的基本概念 1.2.1 API 1.2.2.发送端和接收端 1.2.3 请求和响应 1.2.4 客户端和服务端 2. Socket 套接字 2.1 概念 2.2 分类 3. UDP数据报套接字编程 3.1 DatagramSocket API 3.1.1 含义 3.1.2 构造方法 3…

【C++记忆站】类和对象(一)

类和对象(一) 1.面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题 C是基于面向对象的&#xff0c;关注的是对象&#xff0c;将一件事情拆分成不同的对象&#xff0c;靠对象之间…

计算机:无所不在的角色与跨学科函数概念的生动探索

The Computer’s Ubiquitous Role and the Conceptual Underpinnings of Functions Across Disciplines: A Vivid Exploration 计算机&#xff1a;无所不在的角色与跨学科函数概念的生动探索 A computer is an extraordinary apparatus that has the remarkable ability to exe…

初识HarmonyOS

文章目录 本章节目标一、 HarmonyOS简介初识HarmonyOSHarmonyOS系统定位HarmonyOS典型应用场景 二、HarmonyOS架构与安全1. HarmonyOS架构解析内核层系统服务层框架层应用层应用服务智能分发 2. HarmonyOS系统安全正确的人正确的设备正确地使用数据 三、HarmonyOS关键特性1. 硬…

redis远程连接不上解决办法

问题描述&#xff1a; redis远程服务端运行在192.168.3.90计算机上&#xff0c;客户端计算机&#xff08;ip:192.168.3.110&#xff09;通过redsi-cli.exe客户端工具连接时&#xff0c;没有反应&#xff0c;连接不上。 如图所示&#xff1a; 解决步骤&#xff1a; 步骤一&…

USB-C接口给显示器带来怎样的变化?

随着科技的不断发展&#xff0c;Type-C接口已经成为现代电子设备中常见的接口标准。它不仅可以提供高速的数据传输&#xff0c;还可以实现快速充电和视频传输等功能。因此&#xff0c;使用Type-C接口的显示器方案也受到了广泛的关注。本文将介绍Type-C接口显示器的优势、应用场…

如何进行正确的 CodeReview

软件开发生命周期中至关重要的一步是代码审查。它使开发人员能够显著提升代码质量。它类似于书籍的创作过程。首先&#xff0c;作者写故事&#xff0c;然后经过编辑以确保不会出现诸如混淆“you’re”和“yours”之类的错误。在这个语境中&#xff0c;代码审查指的是检查和评估…

Python项目——计算器(PySide6+Pyinstaller)

1、介绍 使用python编写一个计算器&#xff0c;可以实现基本的运算。【注】该项目最终还有一些细小的bug没有完善&#xff0c;例如符号可以一直输入。 2、实现 使用pyCharm创建一个新的项目。 2.1、设计UI 使用Qt designer设计一个UI界面&#xff0c;保存ui文件&#xff0…

直流过欠压继电器JSZD-1A DC220V 0-220V 面板嵌入式安装

一、 概述 JSZD-1系列直流电压继电器是专为直流系统设计的&#xff0c;以进口大规模集成电路为核心&#xff0c;配以本厂研制的模块和部分外围元件组装而成。具有通用性好、互换性强、寿命长、外形美观大方、性能稳定可靠等特点。 JSZD-1系列直流电压继电器既是一块过、欠压继…

Python 类变量和实例变量详解

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python中&#xff0c;变量分为类变量和实例变量两种类型&#xff0c;它们有着不同的作用范围和生命周期。理解这两种变量类型的区别对于面向对象编程非常重要。本文将详细介绍Python中的类变量和实例变量&…

CentOS7 LAMP环境安装部署Zabbix

CentOS7 LAMP环境安装部署Zabbix 查看CentOS版本号 more /etc/redhat-release server 最好配置静态IP地址、DNS&#xff0c;在DHCP服务器将此IP地址排除掉。 Hostname 改为zabbix 服务端&#xff1a;172.20.26.137 客户端&#xff1a;172.20.26.113 Zabbix服务端安装及配…

3.postman动态参数、文件上传及断言

一、postman内置动态参数以及自定义的动态参数 postman内置动态参数&#xff1a; {{$timestamp}} 生成当前时间的时间戳 {{$randomint}} 生成0-1000之间的随机数 {{$guid}} 生成随机guid字符串 自定义动态参数&#xff1a; 在请求中pre-req页面下 //手动的获得时间戳 var…