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就引入了适配器模式

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

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

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

相关文章

C——语言内存函数

目录 一、memcpy的使用和模拟实现 1.memcpy函数原型 2.memcpy函数的使用 3.memcpy函数的模拟实现 二、memmove的使用和模拟实现 1.memmove函数原型 2.memmove函数的使用 3.memmove函数的模拟实现 三、memset的使用 1.memset函数原型 2.memset函数的使用 四、memcmp…

bash shell基础命令(一)

1.shell启动 shell提供了对Linux系统的交互式访问,通常在用户登录终端时启动。系统启动的shell程序取决于用户账户的配置。 /etc/passwd/文件包含了所有用户的基本信息配置, $ cat /etc/passwd root:x:0:0:root:/root:/bin/bash ...例如上述root账户信…

Python新手常见问题——列表中删不掉的0

1.测试代码 运行代码 nums1 [1,2,3,0,0,0] print(type(nums1))for i in nums1:if i 0:nums1.remove(i) print(nums1)效果如下 2.疑问: 上面代码,为什么把nums1里面的0移除不干净 3.原因: 在 Python 中,不建议在循环中直接…

修改SSH默认端口,使SSH连接更安全

以CentOS7.9为例: 1、修改配置文件 vi /etc/ssh/sshd_config 2、远程电脑可连接,暂时将SELinux关闭 # 查询状态 getenforce # 关闭 setenforce 0 # 开启 setenforce 1 3、SELinux设置(如果启用),semanage管理工具安…

mac上部署单体hbase

1. 简介 HBase 是一个开源的、分布式的、版本化的典型非关系型数据库。它是 Google BigTable 的开源实现,并且是 Apache 基金会的 Hadoop 项目的一部分1。HBase 在 Hadoop Distributed File System (HDFS) 上运行,作为一个列式存储非关系数据库管理系统…

Angular系列教程之变更检测与性能优化

文章目录 前言变更检测的原理脏检查OnPush策略 示例代码总结 前言 Angular 除了默认的变化检测机制,也提供了ChangeDetectionStrategy.OnPush,用 OnPush 可以跳过某个组件或者某个父组件以及它下面所有子组件的变化检测。 在本文中,我们将探…

vscode显示120字符或者80字符提示线或者显示垂直标尺

vscode显示120字符或者80字符提示线或者显示垂直标尺 一般规定一行代码不超过80或者120个字符。取决于团队的编码规范。 不同公司不同团队有不同的规定。 当单行代码过长。产生横向滚动条。使得代码难以阅读。 打开全局设置的settings.json /C:/Users/xxx/AppData/Roaming/Cod…

java音乐交流和周边售卖商城系统springboot+vue

不同的系统提供的服务也不相同,其对应的功能也不相同,所以,系统开工前,需要明确其用途,确定其功能。由此,才可以进行各个任务的开展。 国外摇滚乐队交流和周边售卖系统经过分析,确定了其需要设置…

【昕宝爸爸小模块】图文源码详解什么是线程池、线程池的底层到底是如何实现的

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你👍点赞、🗂️收藏、加❤️关注哦。 本文章CSDN首发,欢迎转载,要注明出处哦! 先感谢优秀的你能认真的看完本文&…

轻松识别Midjourney等AI生成图片,开源GenImage

AIGC时代,人人都可以使用Midjourney、Stable Diffusion等AI产品生成高质量图片,其逼真程度肉眼难以区分真假。这种虚假照片有时会对社会产生不良影响,例如,生成公众人物不雅图片用于散播谣言;合成虚假图片用于金融欺诈…

第一次开发基于SpringBoot的Java应用

第一次开发基于SpringBoot的Java应用 一、 方式1:IDEA创建New Project Spring Boot官方文档的Getting Started1、IDEA创建New Project2、Spring Boot官方文档的Getting Started2.1 Creating the POM (实际是,更新pom.xml)2.2 Add…

【FastAPI】路径参数(二)

预设值 如果你有一个接收路径参数的路径操作,但你希望预先设定可能的有效参数值,则可以使用标准的 Python Enum 类型。 导入 Enum 并创建一个继承自 str 和 Enum 的子类。通过从 str 继承,API 文档将能够知道这些值必须为 string 类型并且能…