Spring之SpringMVC源码

SpringMVC源码

一、SpringMVC的基本结构

1.MVC简介

image.png

以前的纯Servlet的处理方式:

@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String type = req.getParameter(Constant.REQUEST_PARAMETER_TYPE);if(type != null && !"".equals(type)){if(Constant.SERVLET_TYPE_SAVE.equals(type)){// 添加用户信息try {saveOrUpdateUser(req, resp);} catch (Exception e) {e.printStackTrace();}}else if(Constant.SERVLET_TYPE_UPDATE.equals(type)){// 更新用户信息}else if(Constant.SERVLET_TYPE_DELETE.equals(type)){// 删除用户信息deleteUserById(req, resp);}else if(Constant.SERVLET_TYPE_QUEYR.equals(type)){// 查询用户queryUser(req, resp);}else if(Constant.SERVLET_TYPE_QUERYBYID.equals(type)){// 查询单条记录String id = req.getParameter("id");User user = userService.queryById(Integer.parseInt(id));// 跳转到更新的页面同时保存数据到Request作用域中req.setAttribute("user",user);req.getRequestDispatcher("/user/userUpdate.jsp").forward(req,resp);}else if(Constant.SERVLET_TYPE_CHECK.equals(type)){// 验证账号是否存在String userName = req.getParameter("userName");String s = userService.checkUserName(userName);resp.getWriter().println(s);resp.flushBuffer();}}else{// 查询用户信息queryUser(req, resp);}}

为了尽量减少依赖Servlet API,提高程序的可测试性、可复用性而发展出了很多的框架技术:

  • Struts1
  • Struts2
  • SpringMVC

Struts2和SpringMVC的区别

  1. 请求映射上的区别。
  2. 请求数据绑定上的区别。

2.基本结构

  然后我们来看看SpringMVC的基本结构

在这里插入图片描述

从图中我们可以得到的相关信息

  1. 有四个非常重要的角色
  2. DispatchServlet很重要
  3. 谁来负责Controller
  4. DispatchServlet根据什么规则分发请求
  5. View是什么
  6. DispatchServlet负责转发。如何知道怎么转发?
  7. MVC是严格的分工协作

二、控制器

  接下来我们看看应该要如何来设计我们的Controller。控制器的作用是用来具体的处理用户的请求。我们系统能够通过一个普通的bean对象来作为我们的Controller。但是在Spring中对于Bean的管理都是以IoC容器来管理的。这样可以充分的利用SpringIoC和AOP的功能。

问题思考:

  1. 请求如何和Bean对于
  2. 请求如何和Bean的方法对应

1.实例级别的映射

  也就是通过请求地址和Bean的名称对应。通过这个Bean来处理请求。但是如何知道用这个Bean中的哪个方法来处理呢?这时我们可以定义一个接口@Controller.然后声明对应的方法。让Bean去实现这个接口。

public interface Controller {ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

image.png

  接下来我们需要思考:handleRequest这个方法需要返回什么信息?

image.png

  也就是控制器需要返回用户需要的数据和对应的View。那么对应的返回数据应该有什么特点呢?站在我们现在这个角度我们是完全不知道应该要返回什么数据的。完全需要基于用户的需要了。也就是数据需要呈现 多样话。这时可以通过Map来非常灵活的使用。

  同时对应的View也应该具备对应的 多样化的特点。

public interface View {void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response);
}

image.png

  这里的数据和View的串联我们可以自定义一个ModelAndView这个实现来处理

image.png

所以上面的Controller中定义方法的返回值我们就可以定义为ModelAndView了

image.png

还有对应Controller对于客户的响应状态我们可以定义一个 HttpStatus来统一管理

image.png

  这种方式我们可以看到一个Bean对象处理请求的话我们还需要在 handleRequest方法中来判断处理。或者一个Bean处理一个请求,这种方式是非常不灵活的。

2.方法级别的映射

  第二种选择就是具体的请求直接映射到我们对应的方法中。我们定义的普通Bean不需要实现Controller接口了。就作为一个Controller存在。

  那么怎么表示一个Bean是Controller呢?我们需要显示的通过@Controller注解来实现。

在这里插入图片描述

  怎么映射请求到具体的方法中呢?我们可以通过@RequestMapping注解来指定

image.png

然后对应的定义如下:

image.png

3.如何实现多种方式的支持

  上面我们介绍了Controller处理请求的两种方式

image.png

那如何让SpringMVC框架能够支持这两种方式呢?甚至更多的方式

image.png

我们来分析下如何来实现这种需求:

  1. 各种方式的请求映射是不一样的
  2. 各种方式对应的Request Handler 也是不一样的
    1. 方式一:Controller接口的实现对象
    2. @Controller、@RequestMapping注解标识的Bean的方法
  3. 如何设计DispatchServlet来灵活的处理呢

不同的方式,映射的规则不相同,请求处理器也不一样。这时我们可以考虑通过 策略模式来处理了.对应的接口为 HandlerMapping。

image.png

然后将对应的接口的实现者设置为Bean。DispatchServlet从ApplicationContext中获取对应的配置

在这里插入图片描述

  有了对应的handlerMapping然后怎么针对不同的请求来选择对应的实现策略呢,这时我们可以提供对应的适配器来处理。

image.png

  • 每种不同的请求处理器提供它的适配实现,配置为Bean
  • DispatchServlet 从ApplicationContext中获取所有配置。面向HandlerAdapter。隔绝了handler的变化影响。

方式一通过Controller接口处理还是很方便的直接只是 handlerRequest方法就可以直接处理了。但是方式二,我们通过@Controller,@RequestMapping注解来映射到对应的方法这块应该要怎么实现呢?

image.png

分析:方式二的Handler是@Controller、@RequestMapping注解标识的Bean和方法,需要定义一个实体类保存BeanName,方法名。@RequestMapping的注解信息。

在这里插入图片描述

定义 RequestMappingInfo来存储对应的注解信息。那么这个注解信息该由谁去获取呢?并且在什么时候获取呢?这时我们可以在RequestMappingHandlerMapping的getHandler方法之前处理这个解析就可以了。

image.png

我们实现者两个接口来做这个事情就可以了。

	@Overridepublic void afterPropertiesSet() throws Exception {// 检测@Controller Beanfor (String beanName : this.applicationContext.getBeanNamesForType(Object.class)) {Class<?> beanType = this.applicationContext.getType(beanName);if (isHandlerBean(beanType)) {detectHandlerMethod(beanType);}}}

还有一个问题。如果存放检测到的RequestMappingInfo信息呢。如下

image.png

4. DispatchServlet

  到这我们就把Controller怎么找到的路径讲解清楚了,然后来看下DispatchServlet是如何处理的,我们应该怎么来设计。

image.png

我们先来考虑下DispatchServlet要具备哪些功能

  1. 创建ApplicationContext容器
  2. 要从容器中获取HandlerMapping、HandlerAdapter
  3. 完成分发
  4. 完成view转发
  5. 完成异常处理

然后我们得考虑DispatchServlet要完成这些事情,它应该怎么去实现?

image.png

那么对应的操作:

  1. 在init方法中完成3个属性的初始化
  2. 在service方法中完成handler分发、执行的逻辑
  3. 在destory方法中关闭ApplicationContext

对应的DispatcherServlet 应该的结构

image.png

对应的核心代码

	/*** 初始化 MVC相关组件的策略提供者,从applicationContext中获取* * @param applicationContext*/private void initStrategies(ApplicationContext applicationContext) {// 1、initHandlerMappinginitHandlerMappings(applicationContext);// 2、initHandlerAdapterinitHandlerAdapters(applicationContext);}

service方法

	@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 在这里可把一些Dispatcher持有的对象放入到Request中,以被后续处理过程中可能需要使用到req.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE_NAME, webApplicationContext);this.doDispatch(req, resp);}

doDispatch方法

	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Object handler = null;ModelAndView mv = null;Exception dispatchException = null;try {// 1、获取请求对应的handlerhandler = this.getHandler(req);// 2、如果没有对应的handlerif (handler == null) {noHandlerFound(req, resp);return;}// 3、如果有对应的handler,获得handler的AdapterHandlerAdapter ha = this.getHandlerAdapter(handler);// 4、执行adaptermv = ha.handle(req, resp, handler);} catch (Exception e) {dispatchException = e;}// 5、转发给viewprocessDispatchResult(req, resp, handler, mv, dispatchException);}

三、Model&View

  上面介绍请求了怎么分发到Controller来处理请求,接下来我们就需要看看处理完请求后如何响应对应的 数据View

1.View

  我们再回看下前面介绍的HandlerAdapter。

image.png

可以看到对应的handle方法统一返回的是ModelAndView对象。我们需要在方法中创建他的实例,提供对应的View对象。这样Controller的方法的职责就不专一了,被污染了。而且不能灵活的替换View层了。不够灵活。这时我们可以重新定义ModelAndView。再其中加入一个视图名称。

image.png

image.png

加入视图名称后,我们在Controller中的方法返回就可以如下的写法了

image.png

但是有有了一个新的问题,最终的ModelAndView对象由谁来完成呢?根据前面的讲解我们肯定能想到通过 HandlerAdapter来实现了。

	@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {// TODO Auto-generated method stubRequestMappingInfo mappingInfo = (RequestMappingInfo) handler;// ....return null;}

当然这块我们还有一些其他的疑问

  1. Model数据怎么获取?–》参数传递
  2. 还可以直接返回ModelAndView吗? --》完全可以
  3. 返回的是视图名称。怎么转换为View呢?谁来转换呢? --》专门设计一个来处理

3.ViewResolver

  定义一个ViewResolver完成视图名到视图的转换。刚开始不知道怎么干,直接定义一个接口

image.png

  不同的视图技术可能有不同的View实现及转换规则。那就实现ViewResolver来提供对应的转换规则。都配置为Bean。DispatcherServlet可以从容器中获取。

image.png

需要在DispatcherServlet中完成它的初始化以及对应的视图渲染逻辑。

然后来看看ViewResolver的实现。一个基于URL的转发,重定义的ViewResolver实现

image.png

增加其他的ViewResolver实现

image.png

增加其他的View实现

image.png

2.Model

  数据存储Model的设计,相对就比较简单了。

image.png

四、HandlerInterceptor

  上面介绍了那么多还是有问题没有涵盖到,比如请求参数如何绑定到方法参数?需要对请求进行一些过滤,再交给Handler处理。

image.png

这些其实都可以交给HandlerInterceptor来处理的。

public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}
}

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

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

相关文章

【Java面试】十六、并发篇:线程基础

文章目录 1、进程和线程的区别2、并行和并发的区别3、创建线程的四种方式3.1 Runnable和Callable创建线程的区别3.2 线程的run和start 4、线程的所有状态与生命周期5、新建T1、T2、T3&#xff0c;如何保证线程的执行顺序6、notify和notifyAll方法有什么区别7、wait方法和sleep方…

QT-轻量级的笔记软件MyNote

MyNote v2.0 一个轻量级的笔记软件&#x1f4d4; Github项目地址: https://github.com/chandlerye/MyNote/tree/main 应用简介 MyNote v2.0 是一款个人笔记管理软件&#xff0c;没有复杂的功能&#xff0c;旨在提供便捷的笔记记录、管理以及云同步功能。基于Qt 6.6.3 个人开…

MATLAB入门知识

目录 原教程链接&#xff1a;数学建模清风老师《MATLAB教程新手入门篇》https://www.bilibili.com/video/BV1dN4y1Q7Kt/ 前言 历史记录 脚本文件&#xff08;.m&#xff09; Matlab帮助系统 注释 ans pi inf无穷大 -inf负无穷大 i j虚数单位 eps浮点相对精度 0/&a…

Edge浏览器视频画中画可拉动进度条插件Separate Window

平时看一些视频的时候&#xff0c;一般需要编辑一些其他的东西&#xff0c;于是开启画中画&#xff0c;但是画中画没有进度条&#xff0c;需要大幅度的倒退前进得回到原视频&#xff0c;很麻烦。这里推荐插件Separate Window&#xff0c;可实现画中画进度条拉动和播放sudu的调节…

Transparent 且 Post-quantum zkSNARKs

1. 引言 前序博客有&#xff1a; SNARK原理示例SNARK性能及安全——Prover篇SNARK性能及安全——Verifier篇 上图摘自STARKs and STARK VM: Proofs of Computational Integrity。 上图选自&#xff1a;Dan Boneh 斯坦福大学 CS251 Fall 2023 Building a SNARK 课件。 SNARK…

工业通讯现场中关于EtherCAT转TCPIP网关的现场应用

在当今工业自动化的浪潮中&#xff0c;EtherCAT技术以其高效、实时的特性成为了众多制造业的首选。然而&#xff0c;随着工业互联网的发展&#xff0c;对于数据的远程访问和云平台集成的需求日益增长&#xff0c;这就需要将EtherCAT协议转化为更为通用的TCP/IP协议。于是开疆智…

kafka-消费者-消费异常处理(SpringBoot整合Kafka)

文章目录 1、消费异常处理1.1、application.yml配置1.2、注册异常处理器1.3、消费者使用异常处理器1.4、创建生产者发送消息1.5、创建SpringBoot启动类1.6、屏蔽 kafka debug 日志 logback.xml1.7、引入spring-kafka依赖1.8、消费者控制台&#xff1a;1.8.1、第一次启动SpringK…

日进2000,我怎么做到的

昨天遇到一个有意思的项目&#xff0c;让我一天进账2000&#xff0c;一个字&#xff1a;爽。 这几天接洽了一位新客户&#xff0c;主要诉求就是优化系统&#xff0c;基于LNMP的系统优化。正好这个领域我比较熟悉&#xff0c;以前都是在公司做项目&#xff0c;也不怎么涉猎系统优…

HCIP-Datacom-ARST自选题库_10_其他判断【23道题】

1.端到端时延等于路径上所有处理时延与队列时延之和。 2.部署PPP Multilink之后&#xff0c;数据将根据源地址和目的地址均匀的分配在各条成员链路上。 3.流镜像分为本地流镜像和远程流镜像两种方式。√ 4.IP报文中用Tos字段进行Q0S标记&#xff0c;Tos字段中是使用前6bit来…

知识图谱的应用---智能电网

文章目录 智能电网典型应用 智能电网 智能电网以物理电网为基础&#xff0c;将现代先进的传感测量技术、通讯技术、信息技术、计算机技术和控制技术与物理电网高度集成而形成的新型电网。它以充分满足用户对电力的需求和优化资源配置、确保电力供应的安全性、可靠性和经济性、满…

2024.6.9 四

Python的异常处理 在python里,错误和异常是不同的概念 错误: Python 的语法错误或者称之为解析错,大多是因为写代码写错了出现的 异常: 即便 Python 程序的语法是正确的&#xff0c;在运行它的时候&#xff0c;也有可能发生错误。运行期检测到的错误被称为异常。 大多数的异常…

Ajax 快速入门

Ajax 概念&#xff1a;Ajax是一种Web开发技术&#xff0c;允许在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新网页的部分内容。 作用&#xff1a; 数据交换&#xff1a;Ajax允许通过JavaScript向服务器发送请求&#xff0c;并能够接收服务器响应的数据。 异…

STM32H750启动和内存优化(分散加载修改)

前些日子有个朋友一直给我推荐STM32H750这款芯片&#xff0c;说它的性价比&#xff0c;说它多么多么好。于是乎&#xff0c;这两天试了试&#xff0c;嚯&#xff0c;真香&#xff01;我们先看看基本配置 这里简单总结下&#xff0c;cortex-m7内核&#xff0c;128k片内flash …

HTML-CSS练习例子

HTML CSS 练习 https://icodethis.com 作为前端练习生。不敲代码只看&#xff0c;入门是很慢的&#xff0c;所以直接实战是学习前端最快的途径之一。 这个网站练习HTML CSS的&#xff0c;可以打开了解一下&#xff0c;可以每天打卡&#xff0c;例子简单&#xff0c;循序渐进&…

Python第二语言(六、Python异常)

目录 1. 捕获异常&#xff08;try: except: else: finally:&#xff09; 1.1 概念 1.2 基础语法&#xff08;try&#xff1a; except&#xff1a;&#xff09; 1.3 捕获异常&#xff08;异常也有类型&#xff09; 1.4 捕获多个异常&#xff08;try&#xff1a;except(Name…

基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道

作者&#xff1a;尹航 在前文《基于阿里云服务网格流量泳道的全链路流量管理&#xff08;一&#xff09;&#xff1a;严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理&#xff08;二&#xff09;&#xff1a;宽松模式流量泳道》中&#xff0c;我们介绍了流…

大数据数仓的数据回溯

在大数据领域&#xff0c;数据回溯是一项至关重要的任务&#xff0c;它涉及到对历史数据的重新处理以确保数据的准确性和一致性。 数据回溯的定义与重要性 数据回溯&#xff0c;也称为数据补全&#xff0c;是指在数据模型迭代或新模型上线后&#xff0c;对历史数据进行重新处理…

开源多平台AI音乐生成器本地安装结合cpolar内网穿透实现远程访问

文章目录 前言1. 本地部署2. 使用方法介绍3. 内网穿透工具下载安装4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统电脑上快速本地部署一个文字生成音乐的AI创作工具MusicGPT&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问使用。 MusicG…

基于stm32最小版的超声波测距模块

目录 一、模块准备 二、HC-SR04模块原理解释 三、程序完整代码 四、烧录结果 总结 一、模块准备 STM32F103C8T6 HC-SR04 ST-Link&#xff08;其他烧录器也可以&#xff09; 0.96寸OLED屏幕&#xff08;非必须&#xff0c;仅供显示测距结果&#xff0c;可以使用串口助手代替…

nodejs最新某东h5st(4.7.2)参数分析与javascript逆向纯算法还原(含算法源码)(2024-06-09)

一、作者声明&#xff1a; 文章仅供学习交流与参考&#xff01;严禁用于任何商业与非法用途&#xff01;否则由此产生的一切后果均与作者无关&#xff01;如有侵权&#xff0c;请联系作者本人进行删除&#xff01; 二 、写在前面 h5st从4.1一路更新到4.7.2&#xff0c;逐渐vmp…