SpringMVC学习
一、SpringMVC简介和体验
1.1 介绍
https://docs.spring.io/spring-framework/reference/web/webmvc.html
Spring Web MVC是基于Servlet
API构建的原始Web框架,从一开始就包含在Spring
Framework中。正式名称“Spring Web MVC”来自其源模块的名称(
spring-webmvc
),但它通常被称为“Spring MVC”。
在控制层框架历经Strust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC作为Java EE项目表述层开发的首选方案。之所以能做到这一点,是因为SpringMVC具备如下显著优势:
- Spring 家族原生产品,与IOC容器等基础设施无缝对接
- 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
- 代码清新简洁,大幅度提升开发效率
- 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
- 性能卓著,尤其适合现代大型、超大型互联网项目要求
原生Servlet API开发代码片段
1 | protected void doGet(HttpServletRequest request, HttpServletResponse response) |
基于SpringMVC开发代码片段
1 |
|
1.2 主要作用
SSM框架构建起单体项目的技术栈需求!其中的SpringMVC负责表述层(控制层)实现简化!
SpringMVC的作用主要覆盖的是表述层,例如:
- 请求映射
- 数据输入
- 视图界面
- 请求分发
- 表单回显
- 会话控制
- 过滤拦截
- 异步交互
- 文件上传
- 文件下载
- 数据校验
- 类型转换
- 等等等
最终总结:
简化前端参数接收( 形参列表 )
简化后端数据响应(返回值)
以及其他......
1.3 核心组件和调用流程理解
Spring MVC与许多其他Web框架一样,是围绕前端控制器模式设计的,其中中央
Servlet
DispatcherServlet
做整体请求处理调度!
除了DispatcherServlet
SpringMVC还会提供其他特殊的组件协作完成请求处理和响应呈现。
SpringMVC处理请求流程:
SpringMVC涉及组件理解: 1.DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发![ CEO ] 2.HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)和handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler![秘书]
3.HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handler和DispatcherServlet之间的适配器![经理]
4.Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果![打工人]
5.ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的![财务]
二、SpringMVC接收数据
2.1 访问路径设置
@RequestMapping注解的作用就是将请求的 URL 地址和处理请求的方式(handler方法)关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求。
1.精准路径匹配
在@RequestMapping注解指定 URL 地址时,不使用任何通配符,按照请求地址进行精确匹配。
1 |
|
2.模糊路径匹配
在@RequestMapping注解指定 URL 地址时,通过使用通配符,匹配多个类似的地址。
1 |
|
单层匹配和多层匹配: /*:只能匹配URL地址中的一层,如果想准确匹配两层,那么就写“/*/*”以此类推。 /**:可以匹配URL地址中的多层。 其中所谓的一层或多层是指一个URL地址字符串被“/”划分出来的各个层次 这个知识点虽然对于@RequestMapping注解来说实用性不大,但是将来配置拦截器的时候也遵循这个规则。
3.类和方法级别区别
@RequestMapping
注解可以用于类级别和方法级别,它们之间的区别如下:
- 设置到类级别:
@RequestMapping
注解可以设置在控制器类上,用于映射整个控制器的通用请求路径。这样,如果控制器中的多个方法都需要映射同一请求路径,就不需要在每个方法上都添加映射路径。 - 设置到方法级别:
@RequestMapping
注解也可以单独设置在控制器方法上,用于更细粒度地映射请求路径和处理方法。当多个方法处理同一个路径的不同操作时,可以使用方法级别的@RequestMapping
注解进行更精细的映射。
1 | //1.标记到handler方法 |
4.附带请求方式限制
HTTP 协议定义了八种请求方式,在 SpringMVC 中封装到了下面这个枚举类:
1 | public enum RequestMethod { |
默认情况下:@RequestMapping("/logout") 任何请求方式都可以访问!
如果需要特定指定:
1 |
|
注意:违背请求方式,会出现405异常!!!
5.进阶注解
还有 @RequestMapping
的 HTTP 方法特定快捷方式变体:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
1 | // get: 以下两者相等 |
注意:进阶注解只能添加到handler方法上,无法添加到类上!
6.常见配置问题
出现原因:多个 handler 方法映射了同一个地址,导致 SpringMVC 在接收到这个地址的请求时该找哪个 handler 方法处理。
There is already 'demo03MappingMethodHandler' bean method com.atguigu.mvc.handler.Demo03MappingMethodHandler#empGet() mapped.
2.2 接收参数
2.2.1 param 和 json参数比较
在 HTTP 请求中,我们可以选择不同的参数类型,如 param 类型和 JSON 类型。下面对这两种参数类型进行区别和对比:
参数编码: param 类型的参数会被编码为 ASCII 码。例如,假设
name=john doe
,则会被编码为name=john%20doe
。而 JSON 类型的参数会被编码为 UTF-8。参数顺序: param 类型的参数没有顺序限制。但是,JSON 类型的参数是有序的。JSON 采用键值对的形式进行传递,其中键值对是有序排列的。
数据类型: param 类型的参数仅支持字符串类型、数值类型和布尔类型等简单数据类型。而 JSON 类型的参数则支持更复杂的数据类型,如数组、对象等。
嵌套性: param 类型的参数不支持嵌套。但是,JSON 类型的参数支持嵌套,可以传递更为复杂的数据结构。
可读性: param 类型的参数格式比 JSON 类型的参数更加简单、易读。但是,JSON 格式在传递嵌套数据结构时更加清晰易懂。
总的来说,param 类型的参数适用于单一的数据传递,而 JSON 类型的参数则更适用于更复杂的数据结构传递。根据具体的业务需求,需要选择合适的参数类型。在实际开发中,常见的做法是:在 GET 请求中采用 param 类型的参数,而在 POST 请求中采用 JSON 类型的参数传递。
2.2.2 param参数接收
1.直接接值
客户端请求
全写
name不填
age不填
handler接收参数
只要形参数名和类型与传递参数相同,即可自动接收!
1 |
|
2.@RequestParam注解
可以使用 @RequestParam
注释将 Servlet
请求参数(即查询参数或表单数据)绑定到控制器中的方法参数。
@RequestParam
使用场景:
- 指定绑定的请求参数名
- 要求请求参数必须传递
- 为请求参数提供默认值
基本用法:
1 | /** |
默认情况下,使用此批注的方法参数是必需的,但您可以通过将
@RequestParam
批注的 required
标志设置为
false
!
如果没有没有设置非必须,也没有传递参数会出现:
3.特殊场景接值
(1)一名多值
多选框,提交的数据的时候一个key对应多个值,我们可以使用集合进行接收!
1 | /** |
(2)实体接收
Spring MVC 是 Spring 框架提供的 Web 框架,它允许开发者使用实体对象来接收 HTTP 请求中的参数。通过这种方式,可以在方法内部直接使用对象的属性来访问请求参数,而不需要每个参数都写一遍。下面是一个使用实体对象接收参数的示例:
定义一个用于接收参数的实体类:
1 |
|
在控制器中,使用实体对象接收,示例代码如下:
1 | /** |
在上述代码中,将请求参数name和age映射到实体类属性上!要求属性名必须等于参数名!否则无法映射!
2.2.3 路径 参数接收
路径传递参数是一种在 URL 路径中传递参数的方式。在 RESTful 的 Web
应用程序中,经常使用路径传递参数来表示资源的唯一标识符或更复杂的表示方式。而
Spring MVC 框架提供了 @PathVariable
注解来处理路径传递参数。
@PathVariable
注解允许将 URL
中的占位符映射到控制器方法中的参数。
例如,如果我们想将 /user/{id}
路径下的 {id}
映射到控制器方法的一个参数中,则可以使用 @PathVariable
注解来实现。
下面是一个使用 @PathVariable
注解处理路径传递参数的示例:
1 | /** |
2.2.4 json参数接收
前端传递 JSON 数据时,Spring MVC 框架可以使用
@RequestBody
注解来将 JSON 数据转换为 Java
对象。@RequestBody
注解表示当前方法参数的值应该从请求体中获取,并且需要指定 value
属性来指示请求体应该映射到哪个参数上。其使用方式和示例代码如下:
1.前端发送 JSON 数据的示例:(使用postman测试)
1 | { |
2.定义一个用于接收 JSON 数据的 Java 类,例如:
1 |
|
3.在控制器中,使用 @RequestBody
注解来接收 JSON
数据,并将其转换为 Java 对象,例如:
1 |
|
在上述代码中,@RequestBody
注解将请求体中的 JSON
数据映射到 Person
类型的 person
参数上,并将其作为一个对象来传递给 addPerson()
方法进行处理。
4.完善配置
测试:
问题:
原因:
- 不支持json数据类型处理
- 没有json类型处理的工具(jackson)
解决:
springmvc handlerAdpater配置json转化器,配置类需要明确:
1 | /** |
pom.xml 加入jackson依赖
1 | <dependency> |
5.@EnableWebMvc注解说明
@EnableWebMvc注解效果等同于在
XML 配置中,可以使用 <mvc:annotation-driven>
元素!我们来解析<mvc:annotation-driven>
对应的解析工作!
让我们来查看下<mvc:annotation-driven>
具体的动作!
先查看
<mvc:annotation-driven>
标签最终对应解析的Java类查看解析类中具体的动作即可
打开源码:org.springframework.web.servlet.config.MvcNamespaceHandler
打开源码:org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
1 | class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { |
2.3 接收Cookie数据
可以使用 @CookieValue
注释将 HTTP Cookie
的值绑定到控制器中的方法参数。
考虑使用以下 cookie 的请求:
1 | JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 |
下面的示例演示如何获取 cookie 值:
1 |
|
2.4 接收请求头数据
可以使用 @RequestHeader
批注将请求标头绑定到控制器中的方法参数。
请考虑以下带有标头的请求:
1 | Host localhost:8080 |
下面的示例获取 Accept-Encoding
和
Keep-Alive
标头的值:
1 |
|
上面的示例将请求标头的Accept-Encoding、Keep-Alive分别绑定到encoding、keepAlive上
2.5 原生Api对象操作
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html
下表描述了支持的控制器方法参数
Controller method argument 控制器方法参数 | Description |
---|---|
jakarta.servlet.ServletRequest ,
jakarta.servlet.ServletResponse |
请求/响应对象 |
jakarta.servlet.http.HttpSession |
强制存在会话。因此,这样的参数永远不会为 null 。 |
java.io.InputStream , java.io.Reader |
用于访问由 Servlet API 公开的原始请求正文。 |
java.io.OutputStream , java.io.Writer |
用于访问由 Servlet API 公开的原始响应正文。 |
@PathVariable |
接收路径参数注解 |
@RequestParam |
用于访问 Servlet 请求参数,包括多部分文件。参数值将转换为声明的方法参数类型。 |
@RequestHeader |
用于访问请求标头。标头值将转换为声明的方法参数类型。 |
@CookieValue |
用于访问Cookie。Cookie 值将转换为声明的方法参数类型。 |
@RequestBody |
用于访问 HTTP 请求正文。正文内容通过使用
HttpMessageConverter 实现转换为声明的方法参数类型。 |
java.util.Map ,
org.springframework.ui.Model ,
org.springframework.ui.ModelMap |
共享域对象,并在视图呈现过程中向模板公开。 |
Errors , BindingResult |
验证和数据绑定中的错误信息获取对象! |
获取原生对象示例:
1 |
|
2.6 共享域对象操作
#### 2.6.1 属性(共享)域作用回顾
在 JavaWeb 中,共享域指的是在 Servlet 中存储数据,以便在同一 Web
应用程序的多个组件中进行共享和访问。常见的共享域有四种:ServletContext
、HttpSession
、HttpServletRequest
、PageContext
。
ServletContext
共享域:ServletContext
对象可以在整个 Web 应用程序中共享数据,是最大的共享域。一般可以用于保存整个 Web 应用程序的全局配置信息,以及所有用户都共享的数据。在ServletContext
中保存的数据是线程安全的。HttpSession
共享域:HttpSession
对象可以在同一用户发出的多个请求之间共享数据,但只能在同一个会话中使用。比如,可以将用户登录状态保存在HttpSession
中,让用户在多个页面间保持登录状态。HttpServletRequest
共享域:HttpServletRequest
对象可以在同一个请求的多个处理器方法之间共享数据。比如,可以将请求的参数和属性存储在HttpServletRequest
中,让处理器方法之间可以访问这些数据。PageContext
共享域:PageContext
对象是在 JSP 页面Servlet 创建时自动创建的。它可以在 JSP 的各个作用域中共享数据,包括pageScope
、requestScope
、sessionScope
、applicationScope
等作用域。
共享域的作用是提供了方便实用的方式在同一 Web 应用程序的多个组件之间传递数据,并且可以将数据保存在不同的共享域中,根据需要进行选择和使用。
#### 2.6.2 Request级别属性(共享)域
1.使用 Model 类型的形参
1 |
|
2.使用 ModelMap 类型的形参
1 |
|
3.使用 Map 类型的形参
1 |
|
4.使用原生 request 对象
1 |
|
5.使用 ModelAndView 对象
1 |
|
#### 2.6.3 Session级别属性(共享)域
1 |
|
#### 2.6.4 Application级别属性(共享)域
解释:springmvc会在初始化容器的时候,讲servletContext对象存储到ioc容器中!
1 |
|
三、SpringMVC响应数据
3.1 handler方法分析
理解handler方法的作用和组成:
1 | /** |
总结:
请求数据接收,我们都是通过handler的形参列表
前端数据响应,我们都是通过handler的return关键字快速处理!
springmvc简化了参数接收和响应!
3.2 页面跳转控制
#### 3.2.1 快速返回模板视图
1.开发模式回顾
在 Web 开发中,有两种主要的开发模式:前后端分离和混合开发。
前后端分离模式:[重点]
指将前端的界面和后端的业务逻辑通过接口分离开发的一种方式。开发人员使用不同的技术栈和框架,前端开发人员主要负责页面的呈现和用户交互,后端开发人员主要负责业务逻辑和数据存储。前后端通信通过 API 接口完成,数据格式一般使用 JSON 或 XML。前后端分离模式可以提高开发效率,同时也有助于代码重用和维护。
混合开发模式:
指将前端和后端的代码集成在同一个项目中,共享相同的技术栈和框架。这种模式在小型项目中比较常见,可以减少学习成本和部署难度。但是,在大型项目中,这种模式会导致代码耦合性很高,维护和升级难度较大。
对于混合开发,我们就需要使用动态页面技术,动态展示Java的共享域数据!!
2.jsp技术了解
JSP(JavaServer Pages)是一种动态网页开发技术,它是由 Sun 公司提出的一种基于 Java 技术的 Web 页面制作技术,可以在 HTML 文件中嵌入 Java 代码,使得生成动态内容的编写更加简单。
JSP 最主要的作用是生成动态页面。它允许将 Java 代码嵌入到 HTML 页面中,以便使用 Java 进行数据库查询、处理表单数据和生成 HTML 等动态内容。另外,JSP 还可以与 Servlet 结合使用,实现更加复杂的 Web 应用程序开发。
JSP 的主要特点包括:
(1)简单:JSP 通过将 Java 代码嵌入到 HTML 页面中,使得生成动态内容的编写更加简单。
(2)高效:JSP 首次运行时会被转换为 Servlet,然后编译为字节码,从而可以启用 Just-in-Time(JIT)编译器,实现更高效的运行。
(3)多样化:JSP 支持多种标准标签库,包括 JSTL(JavaServer Pages 标准标签库)、EL(表达式语言)等,可以帮助开发人员更加方便的处理常见的 Web 开发需求。
总之,JSP 是一种简单高效、多样化的动态网页开发技术,它可以方便地生成动态页面和与 Servlet 结合使用,是 Java Web 开发中常用的技术之一。
3.准备jsp页面和依赖
pom.xml依赖
1 | <!-- jsp需要依赖! jstl--> |
jsp页面创建
建议位置:/WEB-INF/下,避免外部直接访问!
位置:/WEB-INF/views/index.jsp
1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> |
4.快速响应模版页面
(1)配置jsp视图解析器
1 |
|
(2)handler返回视图
1 | /** |
#### 3.2.2 转发和重定向
在 Spring MVC 中,Handler 方法返回值来实现快速转发,可以使用
redirect
或者 forward
关键字来实现重定向。
1 | /** |
总结:
- 将方法的返回值,设置String类型
- 转发使用forward关键字,重定向使用redirect关键字
- 关键字: /路径
- 注意:如果是项目下的资源,转发和重定向都一样都是项目下路径!都不需要添加项目根路径!
3.3 返回JSON数据(重点)
#### 3.3.1 前置准备
导入jackson依赖
1 | <dependency> |
添加json数据转化器
@EnableWebMvc
1 | //TODO: SpringMVC对应组件的配置类 [声明SpringMVC需要的组件信息] |
#### 3.3.2 @ResponseBody
1.方法上使用@ResponseBody
可以在方法上使用
@ResponseBody
注解,用于将方法返回的对象序列化为 JSON 或 XML
格式的数据,并发送给客户端。在前后端分离的项目中使用!
测试方法:
1 |
|
具体来说,@ResponseBody
注解可以用来标识方法或者方法返回值,表示方法的返回值是要直接返回给客户端的数据,而不是由视图解析器来解析并渲染生成响应体(viewResolver没用)。
返回对象
1 | /** |
返回集合
1 |
|
2.类上使用@ResponseBody
如果类中每个方法上都标记了 @ResponseBody 注解,那么这些注解就可以提取到类上。
1 |
|
#### 3.3.3 @RestController
类上的 @ResponseBody 注解可以和 @Controller 注解合并为 @RestController 注解。所以使用了 @RestController 注解就相当于给类中的每个方法都加了 @ResponseBody 注解。
RestController源码:
1 |
|
3.4 返回静态资源处理
1.静态资源概念
资源本身已经是可以直接拿到浏览器上使用的程度了,不需要在服务器端做任何运算、处理。典型的静态资源包括:
- 纯HTML文件
- 图片
- CSS文件
- JavaScript文件
- ……
2.静态资源访问和问题解决
web应用加入静态资源
手动构建确保编译
访问静态资源
问题分析
- DispatcherServlet 的 url-pattern 配置的是“/”
- url-pattern 配置“/”表示整个 Web 应用范围内所有请求都由 SpringMVC 来处理
- 对 SpringMVC 来说,必须有对应的 @RequestMapping 才能找到处理请求的方法
- 现在 images/mi.jpg 请求没有对应的 @RequestMapping 所以返回 404
问题解决
在 SpringMVC 配置配置类:
1 |
|
再次测试访问图片:
新的问题:其他原本正常的handler请求访问不了了
handler无法访问
解决方案:
1 | @EnableWebMvc //json数据处理,必须使用此注解,因为他会加入json处理器 |
四、RESTFul风格设计和实战
4.1 RESTFul风格概述
#### 4.1.1 RESTFul风格简介
RESTful(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议,广泛应用于现代的Web服务开发。
通过遵循 RESTful 架构的设计原则,可以构建出易于理解、可扩展、松耦合和可重用的 Web 服务。RESTful API 的特点是简单、清晰,并且易于使用和理解,它们使用标准的 HTTP 方法和状态码进行通信,不需要额外的协议和中间件。
总而言之,RESTful 是一种基于 HTTP 和标准化的设计原则的软件架构风格,用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序!
学习RESTful设计原则可以帮助我们更好去设计HTTP协议的API接口!!
#### 4.1.2 RESTFul风格特点
每一个URI代表1种资源(URI 是名词);
客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;
资源的表现形式是XML或者JSON;
客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。
4.1.3 RESTFul风格设计规范
1.HTTP协议请求方式要求
REST 风格主张在项目设计、开发过程中,具体的操作符合HTTP协议定义的请求方式的语义。
操作 | 请求方式 |
---|---|
查询操作 | GET |
保存操作 | POST |
删除操作 | DELETE |
更新操作 | PUT |
2.URL路径风格要求
REST风格下每个资源都应该有一个唯一的标识符,例如一个 URI(统一资源标识符)或者一个 URL(统一资源定位符)。资源的标识符应该能明确地说明该资源的信息,同时也应该是可被理解和解释的!
使用URL+请求方式确定具体的动作,他也是一种标准的HTTP协议请求!
操作 | 传统风格 | REST 风格 |
---|---|---|
保存 | /CRUD/saveEmp | URL 地址:/CRUD/emp 请求方式:POST |
删除 | /CRUD/removeEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:DELETE |
更新 | /CRUD/updateEmp | URL 地址:/CRUD/emp 请求方式:PUT |
查询 | /CRUD/editEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:GET |
总结
根据接口的具体动作,选择具体的HTTP协议请求方式
路径设计从原来携带动标识,改成名词,对应资源的唯一标识即可!
4.1.4 RESTFul风格好处
1.含蓄,安全
使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。
2.风格统一
URL 地址整体格式统一,从前到后始终都使用斜杠划分各个单词,用简单一致的格式表达语义。
3.无状态
在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。
4.严谨,规范
严格按照 HTTP1.1 协议中定义的请求方式本身的语义进行操作。
5.简洁,优雅
过去做增删改查操作需要设计4个不同的URL,现在一个就够了。
操作 | 传统风格 | REST 风格 |
---|---|---|
保存 | /CRUD/saveEmp | URL 地址:/CRUD/emp 请求方式:POST |
删除 | /CRUD/removeEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:DELETE |
更新 | /CRUD/updateEmp | URL 地址:/CRUD/emp 请求方式:PUT |
查询 | /CRUD/editEmp?empId=2 | URL 地址:/CRUD/emp/2 请求方式:GET |
6.丰富的语义
通过 URL 地址就可以知道资源之间的关系。它能够把一句话中的很多单词用斜杠连起来,反过来说就是可以在 URL 地址中用一句话来充分表达语义。
http://localhost:8080/shop/product
4.2 RESTFul风格实战
#### 4.2.1 需求分析
- 数据结构: User {id 唯一标识,name 用户名,age 用户年龄}
- 功能分析
- 用户数据分页展示功能(条件:page 页数 默认1,size 每页数量 默认 10)
- 保存用户功能
- 根据用户id查询用户详情功能
- 根据用户id更新用户数据功能
- 根据用户id删除用户数据功能
- 多条件模糊查询用户功能(条件:keyword 模糊关键字,page 页数 默认1,size 每页数量 默认 10)
4.2.2 RESTFul风格接口设计
1.接口设计
功能 | 接口和请求方式 | 请求参数 | 返回值 |
分页查询 | GET /user | page=1&size=10 | { 响应数据 } |
用户添加 | POST /user | { user 数据 } | {响应数据} |
用户详情 | GET /user/1 | 路径参数 | {响应数据} |
用户更新 | PUT /user | { user 更新数据} | {响应数据} |
用户删除 | DELETE /user/1 | 路径参数 | {响应数据} |
条件模糊 | GET /user/search | page=1&size=10&keywork=关键字 | {响应数据} |
2.问题讨论
为什么查询用户详情,就使用路径传递参数,多条件模糊查询,就使用请求参数传递?
误区:restful风格下,不是所有请求参数都是路径传递!可以使用其他方式传递!
在 RESTful API 的设计中,路径和请求参数和请求体都是用来向服务器传递信息的方式。
- 对于查询用户详情,使用路径传递参数是因为这是一个单一资源的查询,即查询一条用户记录。使用路径参数可以明确指定所请求的资源,便于服务器定位并返回对应的资源,也符合 RESTful 风格的要求。
- 而对于多条件模糊查询,使用请求参数传递参数是因为这是一个资源集合的查询,即查询多条用户记录。使用请求参数可以通过组合不同参数来限制查询结果,路径参数的组合和排列可能会很多,不如使用请求参数更加灵活和简洁。
此外,还有一些通用的原则可以遵循:
路径参数应该用于指定资源的唯一标识或者 ID,而请求参数应该用于指定查询条件或者操作参数。
请求参数应该限制在 10 个以内,过多的请求参数可能导致接口难以维护和使用。
对于敏感信息,最好使用 POST 和请求体来传递参数。
4.2.3 后台接口实现
准备用户实体类:
1 |
|
准备用户Controller:
1 |
|
五、SpringMVC其他扩展
5.1 全局异常处理机制
#### 5.1.1 异常处理两种方式
开发过程中是不可避免地会出现各种异常情况的,例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题,甚至直接导致程序崩溃。因此,在开发过程中,合理处理异常、避免异常产生、以及对异常进行有效的调试是非常重要的。
对于异常的处理,一般分为两种方式:
- 编程式异常处理:是指在代码中显式地编写处理异常的逻辑。它通常涉及到对异常类型的检测及其处理,例如使用 try-catch 块来捕获异常,然后在 catch 块中编写特定的处理代码,或者在 finally 块中执行一些清理操作。在编程式异常处理中,开发人员需要显式地进行异常处理,异常处理代码混杂在业务代码中,导致代码可读性较差。
- 声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过配置等方式进行统一的管理和处理。在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如
@Throws
或@ExceptionHandler
),就可以处理特定类型的异常。相较于编程式异常处理,声明式异常处理可以使代码更加简洁、易于维护和扩展。
站在宏观角度来看待声明式事务处理:
整个项目从架构这个层面设计的异常处理的统一机制和规范。
一个项目中会包含很多个模块,各个模块需要分工完成。如果张三负责的模块按照 A 方案处理异常,李四负责的模块按照 B 方案处理异常……各个模块处理异常的思路、代码、命名细节都不一样,那么就会让整个项目非常混乱。
使用声明式异常处理,可以统一项目处理异常思路,项目更加清晰明了!
#### 5.1.2 基于注解异常声明异常处理
1.声明异常处理控制器类
异常处理控制类,统一定义异常处理handler方法!
1 | /** |
2.声明异常处理hander方法
异常处理handler方法和普通的handler方法参数接收和响应都一致!
只不过异常处理handler方法要映射异常,发生对应的异常会调用!
普通的handler方法要使用@RequestMapping注解映射路径,发生对应的路径调用!
1 | /* |
配置文件扫描控制器类配置
确保异常处理控制类被扫描
1 | <!-- 扫描controller对应的包,将handler加入到ioc--> |
5.2 拦截器使用
#### 5.2.1 拦截器概念
拦截器和过滤器解决问题
生活中
为了提高乘车效率,在乘客进入站台前统一检票
程序中
在程序中,使用拦截器在请求到达具体 handler 方法前,统一执行检测
拦截器 (Springmvc中的) VS 过滤器 (javaWeb中的):
- 相似点
- 拦截:必须先把请求拦住,才能执行后续操作
- 过滤:拦截器或过滤器存在的意义就是对请求进行统一处理
- 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源
- 不同点
- 工作平台不同
- 过滤器工作在 Servlet 容器中
- 拦截器工作在 SpringMVC 的基础上
- 拦截的范围
- 过滤器:能够拦截到的最大范围是整个 Web 应用
- 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求
- IOC 容器支持
- 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的
- 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持
- 工作平台不同
选择:
功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器。
#### 5.2.2 拦截器使用
1.创建拦截器类
1 | public class MyInterceptor implements HandlerInterceptor { |
拦截器方法拦截位置:
2.修改配置类添加拦截器
1 |
|
3.配置详解
(1)默认拦截全部
1 |
|
(2)精准配置
1 |
|
(3)排除配置
1 | //添加拦截器 |
(3)多个拦截器执行顺序
preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置顺序调用各个 preHandle() 方法。
postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 postHandle() 方法。
afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 afterCompletion() 方法。
总结:其实可以将其想象成一个包裹着数据的状态,先加入的拦截器在外层,后加入的拦截器在内存,前面的prehandler方法,外层的自然是远离数据,所以要先执行;后面的posthandler和aftercompletion,外层的自然是远离数据,所以是后执行。
解释:
步骤1:首先在idea中Navigate -> class(ctrl + N),输入DispatcherServlet
步骤2:点进去该类中,点击右边的Structure,找到doDispatch方法,点击
步骤3:查看原理
- prehandler中return true表示放行,return false表示拦截如上图所示,如果prehandler中返回false,那么图中箭头所指的条件就是true,则return,则下面的方法都不执行,从而实现了拦截效果。
- 多个拦截器preHandler、postHandler、afterCompletion的执行顺序问题
- preHandler由上图可以看出,先加的拦截器会先执行prehandler方法
- posthandler由上图可知,由于i是从this.interceptorList.size - 1到0的顺序执行,所以后加入的拦截器会先执行
- aftercompletion和posthandler的原理是一样的
5.3 参数校验
在 Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。
1.校验概述
JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 标准中。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
注解 | 规则 |
---|---|
@Null | 标注值必须为 null |
@NotNull | 标注值不可为 null |
@AssertTrue | 标注值必须为 true |
@AssertFalse | 标注值必须为 false |
@Min(value) | 标注值必须大于或等于 value |
@Max(value) | 标注值必须小于或等于 value |
@DecimalMin(value) | 标注值必须大于或等于 value |
@DecimalMax(value) | 标注值必须小于或等于 value |
@Size(max,min) | 标注值大小必须在 max 和 min 限定的范围内 |
@Digits(integer,fratction) | 标注值值必须是一个数字,且必须在可接受的范围内 |
@Past | 标注值只能用于日期型,且必须是过去的日期 |
@Future | 标注值只能用于日期型,且必须是将来的日期 |
@Pattern(value) | 标注值必须符合指定的正则表达式 |
JSR 303 只是一套标准,需要提供其实现才可以使用。Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:
注解 | 规则 |
---|---|
标注值必须是格式正确的 Email 地址 | |
@Length | 标注值字符串大小必须在指定的范围内 |
@NotEmpty | 标注值字符串不能是空字符串 |
@Range | 标注值必须在指定的范围内 |
Spring 4.0 版本已经拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在SpringMVC 中,可直接通过注解驱动 @EnableWebMvc 的方式进行数据校验。Spring 的 LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在Spring容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean中。Spring本身并没有提供JSR 303的实现,所以必须将JSR 303的实现者的jar包放到类路径下。
配置 @EnableWebMvc后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Validated 注解即可让 SpringMVC 在完成数据绑定后执行数据校验的工作。
2.操作演示
- 导入依赖
1 | <!-- 校验注解 --> |
- 应用校验注解
1 | /** |
- handler标记和绑定错误收集
1 |
|
测试效果
- 正确
- 错误
3.易混总结
@NotNull、@NotEmpty、@NotBlank 都是用于在数据校验中检查字段值是否为空的注解,但是它们的用法和校验规则有所不同。
@NotNull (包装类型不为null):@NotNull 注解是 JSR 303 规范中定义的注解,当被标注的字段值为 null 时,会认为校验失败而抛出异常。该注解不能用于字符串类型的校验,若要对字符串进行校验,应该使用 @NotBlank 或 @NotEmpty 注解。
@NotEmpty (集合类型长度大于0):@NotEmpty 注解同样是 JSR 303 规范中定义的注解,对于 CharSequence、Collection、Map 或者数组对象类型的属性进行校验,校验时会检查该属性是否为 Null 或者 size()==0,如果是的话就会校验失败。但是对于其他类型的属性,该注解无效。需要注意的是只校验空格前后的字符串,如果该字符串中间只有空格,不会被认为是空字符串,校验不会失败。
@NotBlank (字符串,不为null,切不为" "字符串):@NotBlank 注解是 Hibernate Validator 附加的注解,对于字符串类型的属性进行校验,校验时会检查该属性是否为 Null 或 “” 或者只包含空格,如果是的话就会校验失败。需要注意的是,@NotBlank 注解只能用于字符串类型的校验。
总之,这三种注解都是用于校验字段值是否为空的注解,但是其校验规则和用法有所不同。在进行数据校验时,需要根据具体情况选择合适的注解进行校验。