SpringMVC 源码剖析

SpringMVC 源码剖析

在这里插入图片描述

0 从源码角度分析SpringMVC执行流程

// 前端控制器,SpringMVC最核心的类
public class DispatcherServlet extends FrameworkServlet {// 前端控制器最核心的方法,这个方法是负责处理请求的,一次请求,调用一次 doDispatch 方法。protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 通过请求获取处理器// 请求:http://localhost:8080/springmvc/hello (有URI)// 根据请求路径来获取对应的要执行的处理器// 实际上返回的是一个处理器执行链对象// 这个执行链(链条)把谁串起来了呢?把这一次请求要执行的所有拦截器和处理器串起来了。// HandlerExecutionChain是一次请求对应一个对象HandlerExecutionChain mappedHandler = getHandler(request);// 根据处理器获取处理器适配器对象HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Handler就是我们写的Controller// 执行该请求对应的所有拦截器中的 preHandle 方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 调用处理器方法,返回ModelAndView对象// 在这里进行的数据绑定,实际上调用处理器方法之前要给处理器方法传参// 需要传参的话,这个参数实际上是要经过一个复杂的数据绑定过程(将前端提交的表单数据转换成POJO对象)mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 执行该请求对应的所有拦截器中的 postHandle 方法mappedHandler.applyPostHandle(processedRequest, response, mv);// 处理分发结果(本质上就是响应结果到浏览器)processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {// 渲染render(mv, request, response);// 执行该请求所对应的所有拦截器的afterCompletion方法mappedHandler.triggerAfterCompletion(request, response, null);}protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {// 通过视图解析器进行解析,返回视图View对象View view = resolveViewName(viewName, mv.getModelInternal(), locale, request);// 调用视图对象的渲染方法(完成响应)view.render(mv.getModelInternal(), request, response);}protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,Locale locale, HttpServletRequest request) throws Exception {// 视图解析器ViewResolver viewResolver;// 通过视图解析器解析返回视图对象ViewView view = viewResolver.resolveViewName(viewName, locale);}
}// 视图解析器接口
public interface ViewResolver {View resolveViewName(String viewName, Locale locale) throws Exception;
}// 视图解析器接口实现类也很多:ThymeleafViewResolver、InternalResourceViewResolver// 视图接口
public interface View{void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception;
}// 每一个接口肯定是有接口下的实现类,例如View接口的实现类:ThymeleafView、InternalResourceView....

1 关于根据请求获取处理器执行链

分析这一行代码:
HandlerExecutionChain mappedHandler = getHandler(request);1. HandlerExecutionChain:处理器执行链对象2. HandlerExecutionChain中的属性:public class HandlerExecutionChain{// 底层对应的是一个HandlerMethod对象// 处理器方法对象Object handler = new HandlerMethod(.....);// 该请求对应的所有的拦截器按照顺序放到了ArrayList集合中// 所有的拦截器对象也都是在服务器启动的时候都创建好。List<HandlerInterceptor> interceptorList;}3. HandlerMethod 是什么?HandlerMethod是最核心的要执行的目标,翻译为:处理器方法。注意:HandlerMethod 是在web服务器启动时初始化spring容器的时候,就创建好了。这个类当中比较重要的属性包括:beanName和Method例如,以下代码:@Controller("userController")public class UserController{@RequestMapping("/login")public String login(User user){return ....}}那么以上代码对应了一个HandlerMethod对象:public class HandlerMethod{private String beanName = "userController";private Method loginMethod;}4. getHandler(request);这个方法还是在DispatcherServlet类中。protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {// 通过合适的 HandlerMapping才能获取到 HandlerExecutionChain对象。// 如果你处理器方法使用了 @RequestMapping注解,那么以下代码中的mapping是:RequestMappingHandlerMapping对象。HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}重点:我们处理请求的第一步代码是:HandlerExecutionChain mappedHandler = getHandler(request);其本质上是调用了:HandlerExecutionChain handler = mapping.getHandler(request);mapping变量就是 HandlerMappingHandlerMapping是一个接口:翻译为处理器映射器,专门负责映射的。就是本质上根据请求路径去映射处理器方法的。HandlerMapping接口下有很多实现类:例如其中一个比较有名的,常用的:RequestMappingHandlerMapping这个 RequestMappingHandlerMapping 叫做:@RequestMapping注解专用的处理器映射器对象。当然,如果你没有使用 @RequestMapping注解,也可以写xml配置文件来进行映射,那个时候对应的就是其他的HandlerMapping接口的实现类了。HandlerMapping 对象也是在服务器启动阶段创建的,所有的HandlerMapping对象都是在服务器启动阶段创建,并且存放到集合中。public class DispatcherServlet{List<HandlerMapping> handlerMappings;}5. RequestMappingHandlerMapping中的 getHandler(request);HandlerExecutionChain handler = mapping.getHandler(request);mapping.getHandler(request);这个方法底层一定是获取了 HandlerMethod 对象,将其赋值给 HandlerExecutionChain的handler属性public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping{protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {super.registerHandlerMethod(handler, method, mapping);updateConsumesCondition(mapping, method);}}public class AbstractHandlerMethodMapping{protected void registerHandlerMethod(Object handler, Method method, T mapping) {this.mappingRegistry.register(mapping, handler, method);}public void register(T mapping, Object handler, Method method) {HandlerMethod handlerMethod = createHandlerMethod(handler, method);}protected HandlerMethod createHandlerMethod(Object handler, Method method) {if (handler instanceof String beanName) {return new HandlerMethod(beanName,obtainApplicationContext().getAutowireCapableBeanFactory(),obtainApplicationContext(),method);}return new HandlerMethod(handler, method);}}这一步牵连到的类有哪些:HandlerExecutionChainHandlerMethodHandlerInterceptorHandlerMappingRequestMappingHandlerMapping(是HandlerMaping接口的实现)

2 关于根据处理器来获取处理器适配器

分析:
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());1. 底层使用了适配器模式。2. 每一个处理器(我们自己写的Controller),都有自己适合的处理器适配器。3.SpringMVC当中处理器适配器也有很多种,其中一个比较有名的,常用的处理器适配器是:RequestMappingHandlerAdapter
这个处理器适配器是专门处理 “处理器方法”上有 @RequestMapping 注解的。4. mappedHandler.getHandler() 获取的是 HandlerMethod 对象5. HandlerAdapter也是一个接口:其中有一个常用的实现类:RequestMappingHandlerAdapter6. 在服务器启动阶段,所有的 HandlerAdapter接口的实现类都会创建出来。在服务器启动阶段!!!!!!List<HandlerAdapter> handlerAdapters;7. HandlerAdapter接口非常重要,通过这个接口来调用最终的 HandlerMethod8. HandlerAdapter是适配器,是对 HandlerMethod 进行的适配。9.DispatcherServlet类中,如下代码:protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}}

3 关于执行请求对应的拦截器preHandle

关于执行请求对应的拦截器的preHandle方法DispatcherServlet:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;
}HandlerExecutionChainboolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;
}遍历List集合,从List集合中取出每一个 HandlerInterceptor对象,调用 preHandle,i++,可见是顺序调用。

4 关于调用处理器方法

关于调用处理器方法:mv = ha.handle(processedRequest, response, mappedHandler.getHandler());ha 是处理器适配器mv 是ModelAndView对象这个方法是最核心的,调用请求路径对应的HandlerMethod。(调用处理器方法。)ha是HandlerAdapter,如果是 @RequestMapping 注解对应的,那么就是 RequestMappingHandlerAdapterRequestMappingHandlerAdapterprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {mav = invokeHandlerMethod(request, response, handlerMethod);}protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {// 获取一个数据绑定工厂WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);// 获取一个可调用的处理器方法ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 给可调用的方法绑定数据invocableMethod.setDataBinderFactory(binderFactory);// 给可调用的方法设置参数invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 可调用的方法执行了。invocableMethod.invokeAndHandle(webRequest, mavContainer);}HandlerAdapter中做的核心事情:将前端提交的form数据通过 HttpMessageConverter 将其转换成 POJO对象。(数据转换)并将数据绑定到 HandlerMethod 对象上。调用HandlerMethod。返回 ModelAndView

5 关于执行请求对应的拦截器的postHandle

DispatcherServlet:mappedHandler.applyPostHandle(processedRequest, response, mv);HandlerExecutionChain:void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}}通过源码解决,可以很轻松的看到,从List集合中逆序(i--)逐一取出拦截器对象,并且调用拦截器的 postHandle方法。

6 关于处理分发结果

public class DispatcherServlet{// 处理分发结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {// 渲染render(mv, request, response);}protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {// 通过视图解析器进行解析,返回视图View对象View view = resolveViewName(viewName, mv.getModelInternal(), locale, request);// 调用视图对象的渲染方法(完成响应)view.render(mv.getModelInternal(), request, response);}protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,Locale locale, HttpServletRequest request) throws Exception {// 视图解析器ViewResolver viewResolver;// 通过视图解析器解析返回视图对象ViewView view = viewResolver.resolveViewName(viewName, locale);}
}// 视图解析器接口
public interface ViewResolver {View resolveViewName(String viewName, Locale locale) throws Exception;
}// 视图解析器接口实现类也很多:ThymeleafViewResolver、InternalResourceViewResolver// 视图接口
public interface View{void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception;
}// 每一个接口肯定是有接口下的实现类,例如View接口的实现类:ThymeleafView、InternalResourceView....

7 关于执行拦截器的afterCompletion方法

DispatcherServlet:private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {// 渲染render(mv, request, response);// 执行该请求所对应的所有拦截器的afterCompletion方法mappedHandler.triggerAfterCompletion(request, response, null);}HandlerExecutionChain:void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);}catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}通过源码可以看出,也是通过逆序(i--)的方式进行拦截器的调用,调用拦截器的afterCompletion方法。

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

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

相关文章

C语言趣味代码(三)

这一篇主要围绕写一个程序---寻找数字 来写&#xff0c;在这篇我会详细和大家介绍基本实现以及它的改良版&#xff0c;还有相关知识的拓展&#xff0c;干货绝对满满。 1. 寻找数字 在这一主题下&#xff0c;我们会编写一些代码&#xff0c;来锻炼玩家的反应力&#xff0c;同时…

【python源码解析】深入 Pandas BlockManager 的数据结构和初始化过程

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 python数据分析…

Vue2学习笔记(尚硅谷天禹老师)

目录 一、入门案例 二、模板语法 三、数据绑定 四、el和data的两种写法 五、MVVM模型 六、Object.defineproperty方法 七、Vue中响应式原理 八、数据代理 九、methods配置项 十、Vue中的事件处理 十一、Vue中的键盘事件 十二、计算属性 十三、监视属性watch 十四、绑定Class样式…

数据库构建中的三范式设计(附SQL实例说明)

数据库构建中的三范式&#xff08;附SQL实例说明&#xff09; 设计数据库时遵循三范式&#xff08;1NF、2NF、3NF&#xff09;是关系型数据库设计中用于减少数据冗余、提高数据一致性的理论基础。 第一范式&#xff1a;任何一张表必须有主键&#xff0c;每个字段原子性不可再分…

主控制文件(项目的开始)食用顺序_3

遇到问题加QQ群聊 群主在线解答 点击加入群聊【星辰开发】 该文档 如有更新 会直接修改此文档 目前版本 2024.4.23 本项目需求 控制文件 尽量不修改 代码要简介快速 控制文件代码如下 from flask import Flask, request# 三种事件扩展包 自行创建 import Group # 群聊事…

玩转微服务-SonarQube

这里写目录标题 第一节 SonarQube1.1 简介1.2 四个组成部分1.2.1 SonarQube服务器1.2.2 SonarQube数据库1.2.3 插件1.2.4 Scanner 1.3 工作流程 第二节 SonarQube的安装2.1 安装2.2 插件 第三节 P3C规范3.1 简介3.2 SonarQube 配置 P3C规范3.3 IDEA配置 P3C规范 第四节 Maven项…

Mybatis-动态SQL

黑马程序员JavaWeb开发教程 文章目录 一、Mybatis-XML映射文件1、XML映射文件&#xff08;1&#xff09;规范&#xff08;2&#xff09;MybatisX 二、Mybatis-动态SQL-if1、动态SQL2、 标签<if><where>3、示例 三、Mybatis-动态SQL-foreach根据 id 批量删除员工1、…

踩坑npm install qrcodejs2和crypto-js

在VSCode输入以下命令时&#xff1a; npm run dev 报错找不到crypto-js依赖。 通过以下命令安装crypto-js依赖。 npm install --save crypto-js 然后再次运行启动命令继续报错&#xff0c;这次报错找不到qrcodejs2依赖&#xff0c;通过以下命令安装qrcodejs2依赖&#xff…

iOS - 多线程-atomic

文章目录 iOS - 多线程-atomic1. 源码分析1.1 get方法1.2 set方法 2. 一般不使用atomic的原因 iOS - 多线程-atomic atomic用于保证属性setter、getter的原子性操作&#xff0c;相当于在getter和setter内部加了线程同步的锁可以参考源码objc4的objc-accessors.mm它并不能保证使…

刚刚,ChatGPT重大更新!GPT-4更加聪明,已成毕业论文撰写润色修改最佳利器!

今天ChatGPT Plus版本做了升级。GPT-4增强了记忆功能&#xff0c;能够通过你的多次对话了解你的意图&#xff0c;并提供合适的回应&#xff0c;总结一句话就是&#xff1a;更加懂你&#xff01; ChatGPT到底能干什么&#xff1f;我简单总结一下&#xff1a; 翻译&#xff1a;中…

电商技术揭秘三十一:智能风控与反欺诈技术

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘二十八&#xff1a;安全与合规性保障 电商技术揭秘二十九&#xff1a;电商法律合规浅析 电商技术揭秘三十&#xff1a;知识产权保…

C++必修:从C到C++的过渡(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. 缺省参数 1.1. 缺省参数的使用 缺省参数是声明或定义函数时为函数的参数指定…

Java基础之JVM对象内存分配机制简介

一 对象内存分配 1.1 运行时数据区域 1.2 常见java应用启动JVM参数&#xff1a; -Xss&#xff1a;每个线程的栈大小(单位kb)-Xms&#xff1a;堆的初始大小&#xff0c;默认物理内存的1/64,示例&#xff1a;-Xms:4g -Xms:10m-Xmx&#xff1a;堆的最大可用大小&#xff0c;默认物…

(Java)队列

一.概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾&#xff08;Tail/Rear&#xff09; 出队列&#xff1a;进行删…

数据的正态性检验

正态性检验 判断 pd.Series 数据是否符合正态分布&#xff0c;可以采用以下几种方法。 计算偏度 偏度&#xff08;Skewness&#xff09;是衡量数据分布对称性的统计量。如果偏度大于 0&#xff0c;则数据分布是右偏的&#xff08;正偏斜&#xff09;&#xff1b;如果偏度小于…

时尚新选择,小塔RFID技术重塑样衣管理

在时尚领域&#xff0c;样衣是创意与工艺的完美结合&#xff0c;每一件都承载着设计师的心血与期待。然而&#xff0c;当这些珍贵的样版在传统的管理体系下流转时&#xff0c;样版管理成为一个令人头疼的问题。手动记录、盘点和样板追溯成为常态&#xff0c;但这种方式容易出错…

亚远景科技-ASPICE评估师等级、ASPICE评估师培训和ASPICE评估项目等级简介

1. ASPICE评估师等级&#xff1a; ASPICE评估师等级主要包括助理评估师、主任评估师、首席评估师和评估师导师。这些等级反映了评估师在执行ASPICE评估项目时的经验和专业能力。助理评估师即小PA&#xff0c;主任评估师即CA,首席评估师即大PA。每一个等级都需要经过严格的培训…

Web前端开发之HTML_1

第一个前端程序VS Code安装VS Code 快捷键 1. 第一个前端程序 使用记事本&#xff0c;新建一个文本文档&#xff0c;重命名为Welcome.html&#xff0c;如下图&#xff1a; 用记事本打开文档&#xff0c;内容输入如下&#xff1a; <html> <head> <t…

C++11 数据结构6 栈的链式存储,实现,测试

栈顶放在链表的头部 栈顶放在链表的头部还是尾部呢&#xff1f; 需要栈 是特殊的线性表&#xff0c;那么我们回忆一下 线性表的链式存储的插入和删除的写法&#xff0c;就应该能理清线性表的头部做为栈顶 合适 还是 线性表的尾部 作为栈顶合适 插入算法 核心代码 //正式插入…

深度学习| 注意力机制

注意力机制 为什么需要注意力机制Seq2Seq问题Transfomer Attention注意力机制分类软硬注意力注意力域 为什么需要注意力机制 这个可以从NLP的Seq2Seq问题来慢慢理解。 Seq2Seq问题 Seq2Seq&#xff08;Sequence to Sequence&#xff09;&#xff1a;早期很多模型中&#xff…