Spring之SpringMVC源码

news2024/11/24 18:00:50

SpringMVC源码

一、SpringMVC的基本结构

1.MVC简介

image.png

以前的纯Servlet的处理方式:

@Override
    protected 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

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

	@Override
	public void afterPropertiesSet() throws Exception {
		// 检测@Controller Bean
		for (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、initHandlerMapping
		initHandlerMappings(applicationContext);

		// 2、initHandlerAdapter
		initHandlerAdapters(applicationContext);

	}

service方法

	@Override
	protected 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、获取请求对应的handler
			handler = this.getHandler(req);
			// 2、如果没有对应的handler
			if (handler == null) {
				noHandlerFound(req, resp);
				return;
			}
			// 3、如果有对应的handler,获得handler的Adapter

			HandlerAdapter ha = this.getHandlerAdapter(handler);

			// 4、执行adapter
			mv = ha.handle(req, resp, handler);

		} catch (Exception e) {
			dispatchException = e;
		}
		// 5、转发给view
		processDispatchResult(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来实现了。

	@Override
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// TODO Auto-generated method stub
		RequestMappingInfo 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.coloradmin.cn/o/1807068.html

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

相关文章

Autodesk 3ds Max软件下载安装;3ds Max功能强大的三维建模、渲染软件安装包获取

3ds Max&#xff0c;无论是初学者还是资深设计师&#xff0c;都能通过3ds Max在数字世界中实现自己的创意&#xff0c;打造出令人惊叹的三维作品。 在3ds Max中&#xff0c;灯光系统是至关重要的一环。它提供了光度学灯光和标准灯光两种主要类型&#xff0c;用于照亮和增强场景…

预备知识----技术架构演进之路

单机架构 简介&#xff1a;应用服务和数据库服务共用一台服务器。 出现原因&#xff1a;出现在互联网早期&#xff0c;访问量较小&#xff0c;单机足以满足需求。 架构工作原理&#xff1a;通过应用&#xff08;划分了多个模块&#xff09;和数据库在单个服务器上写作完成业务…

Liunx环境下redis主从集群搭建(保姆级教学)01

Linux 环境安装redis 准备一台linux虚拟机 我使用基于Linux的开源类服务器操作系统CentOS7。 打开虚拟机&#xff0c;输入密码登录 下载linux版本的redis安装包 已经下载redis-5.0.10.tar.gz 创建一个文件夹用来安装redis,我在/opt目录下创建redis文件夹 将下载好的redis…

前端_调试工具_Chrome Devtools

目录 一、上左侧菜单功能 1.选择功能 2.手机/电脑 显示切换功能 3.内存 4.元素 5.控制台 6.源代码/来源 7.网络 8.应用 9.性能 10.安全 11.Lighthouse 12.记录器 13.性能数据分析 二、上右侧菜单功能 1.警告 2.设置 3....更多功能 谷歌浏览器自带了调试工具C…

AE电源pinnacle软件新款老款二款软件

AE电源pinnacle软件新款老款二款软件

数据中心运维管理方案

数据中心运维管理方案 随着数据中心在现代信息社会中的重要性日益增加&#xff0c;高效、可靠的运维管理方案成为保障其稳定运行的关键。本文将探讨数据中心运维管理的策略和实践&#xff0c;旨在为运维团队提供全面、系统的管理方法&#xff0c;确保数据中心在任何情况下都能…

(文章复现)低温环境下考虑电池寿命的微电网优化调度

参考文献&#xff1a; [1]丁佳昀,胡秦然,吴在军,等.低温环境下考虑电池寿命的微电网优化调度[J].中国电机工程学报,2024,44(10):3815-3824. 1.摘要 储能系统作为微电网重要组成部分&#xff0c;为微电网协调能量供需提供了解决方案。然而&#xff0c;在低温环境下&#xff0c…

抽象类接口(超详细)

抽象类&接口(超详细) 一:抽象类和抽象方法 封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为 如果老师的work是教书,学生的work是学习。以前我们是在父类里面写一个work随便写一个方法体,然后子类重写。但是有一个弊端,如果子类不是你写的,是别人写…

嵌入式仪器模块:波形发生器模块(嵌入式)

• 16 位分辨率 • 125 MHz 刷新率 • 支持生成 FSK/ASK 信号 应用场景 • 生成任意标准波形或用户自定义波形 • 在特殊协议通信中模拟某个波形 • 无线充电&#xff08;信号调制&#xff09; 道114输出阻抗Low-ZLow-ZLow-Z输出范围 5 V 5 V 6 V耦合DCDCDC带宽4 MHz10 M…

第一个SpringBoot项目

目录 &#x1f4ad;1、新建New Project IDEA2023版本创建Sping项目只能勾选17和21&#xff0c;却无法使用Java8&#xff1f;&#x1f31f; 2、下载JDK 17&#x1f31f; &#x1f4ad;2、项目创建成功界面 1、目录 &#x1f31f; 2、pom文件&#x1f31f; &#x1f4ad;3、…

C#操作MySQL从入门到精通(20)——更新数据

前言: 谈到数据库,大家最容易脱口而出的就是增删改查,本文所说的更新数据就是增删改查的改,改变数据的意思。 本文测试使用的数据库如下: 1、更新一列 所谓更新一列的意思就是只更改一列数据,并且通常要使用where条件,因为不加这个条件的话会导致将所有行的数据进行…

LeetCode:环形链表II

文章收录于LeetCode专栏 LeetCode地址 环形链表II 题目 给定一个链表&#xff0c;返回链表开始入环的第一个节点。如果链表无环&#xff0c;则返回null。   为了表示给定链表中的环&#xff0c;我们使用整数pos来表示链表尾连接到链表中的位置&#xff08;索引从0开始&#…

vue3中进度条上加高亮圆点

实现效果 小圆点基于进度条定位&#xff08;left&#xff09;。 实现代码 <template><!-- 这块代码实现的功能&#xff1a;progressData遍历的年份进度数组&#xff0c;展示每年完成的进度--><ul><li v-for"(item, index) in progressData" :k…

LabVIEW 反向工程的实现与法律地位

什么是LabVIEW反向工程&#xff1f; 反向工程是指从现有的应用程序或软件中推导出其设计、架构、代码等信息的过程。对于LabVIEW而言&#xff0c;反向工程涉及从现有的VI&#xff08;虚拟仪器&#xff09;文件、项目或应用程序中提取出设计思路、功能模块、算法实现等。 LabV…

轻松上手MYSQL:SQL优化之Explain详解

​​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 文章目录 一、Explain1.1 explain作用1.2 explain列说明idselect_typetableparti…

Linux|如何安装 Java

引言 Java是最受欢迎的编程语言之一&#xff0c;JVM&#xff08;Java的虚拟机&#xff09;是运行Java应用程序的运行时环境。这两个平台是许多流行软件所需的&#xff0c;包括Tomcat&#xff0c;Jetty&#xff0c;Cassandra&#xff0c;Glassfish和Jenkins。 本教程[1]将指导您…

关闭Cloudflare Pages的访问策略

curl API 获取相应的 uid curl -X GET "https://api.cloudflare.com/client/v4/accounts/账户标识符/access/apps" \-H "X-Auth-Email: 邮箱" \-H "X-Auth-Key: Global API KEY" \-H "Content-Type: application/json"账户标识符是登…

C# WPF入门学习主线篇(十九)—— 布局管理实战『混合布局案例』

C# WPF入门学习主线篇&#xff08;十九&#xff09;—— 布局管理实战『混合布局案例』 欢迎来到C# WPF入门学习系列的第十九篇。在前几篇文章中&#xff0c;我们详细介绍了各个布局容器的基本概念和使用方法。本篇博客将通过一个综合的实战案例&#xff0c;展示如何在WPF中使用…

Python第二语言(五、Python文件相关操作)

目录 1. 文件编码的概念 2. 文件的读取操作 2.1 什么是文件 2.2 open()打开函数 2.3 mode常用的三种基础访问模式 2.4 文件操作及案例 3. 文件的写入操作及刷新文件&#xff1a;write与flush 4. 文件的追加操作 5. 文件操作的综合案例&#xff08;文件备份操作&#x…

【SQLAlChemy】常见的数据类型有哪些,Column可选的参数有哪些呢?

常见数据类型与Column参数 常见类型 Integer&#xff1a;整数类型&#xff0c;对应数据库的 int 类型。Float&#xff1a;浮点数类型&#xff0c;对应数据库的 float 类型。它占用 32 位空间。Double&#xff1a;双精度浮点数类型&#xff0c;对应数据库的 double 类型&#…