文章目录
系列文章目录
前言
提示:
本博客的内容是在上篇博客SpringMVC学习指南(1)的基础上更新的,没有看过上一篇的同学请移步看完上一篇博客之后,再进行本篇博客的阅读.
本篇博客的会用大量的代码实例带你快速掌握SpringMVC开发的几个基本且重要的注解。希望大家在看博客的同时,多动手敲,多调试,便能更好的学会SpringMVC。
一、RequestMapping注解
该注解有四个常用属性,分别是:
属性名 | 类型 | 含义 |
---|---|---|
value | string[] | 请求映射 |
method | RequestMethod[] | 请求方式 |
params | string[] | 请求参数 |
header | string[] | 请求头 |
1.1 value属性
(1)含义
- 指定请求映射路径,SpringMVC中一个方法处理一个请求。这里的这个请求映射路径就对应前端发起的某个请求。
(2)有两个作用:
- 标注在类上:为下面的方法设置了一个基准路径,即:类下的所有方法都会有一个前缀地址。
- 标注在方法上:对应一个请求路径。即,该方法要处理的是哪个请求。
(3)实例:
在前端页面点击超链接,发起请求 /hello ,后端myFirstRequest()方法上标注了 @RequestMapping(value
= “/hello”) 注解,就表示该方法是用来处理 /hello 请求的。
处理完成之后,方法会返回一个字符串 “success”,我们配置的视图解析器(InternalResourceViewResolver)会完成拼串操作,即:会转发到一个success.jsp页面。
<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<html>
<body>
<h1>首页</h1>
<a href="hello">HelloWorld测试</a><hr>
</body>
</html>
@RequestMapping(value = "/hello")
public String myFirstRequest()
{
System.out.println("hello请求正在处理……");
//视图解析器自动拼串,拿到它的前后缀。
//转发到 /WEB-INF/pages/success.jsp 页面
return "success";
}
1.2 method属性
(1)含义
- 限定请求方式(GET请求、POST请求),默认是都接收,不区分GET和POST。
- 如果设置了method的值,就只支持设置的请求方式,如果以其他的方式发起的请求,浏览器就会报错 405-Method Not Allowed
(2)method的四个取值
- RequestMethod.GET
- RequestMethod.POST
- RequestMethod.DELETE
- RequestMethod.PUT
- 这些请求方式有什么用呢?
在后文中我们会使用RUST风格的URL,到时候就会使用不同的请求方式发起请求。同一个URL,它发起的请求方式不同,后端的处理方式就不同。
(3)实例
当然,这里设置method方式为GET是多此一举的,要求前端发起的超链接请求本就是GET方式的,这里只是为了说明如何使用。
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String myFirstRequest()
{
System.out.println("请求正在处理…… success");
return "success";
}
1.3 params属性
(1)含义
-
规定请求参数,请求路径中必须有参数,如:?username=1
-
params属性 和 header属性 支持简单的表达式。
- param:表示请求必须包含名为 param 的参数
- !param: 表示请求不能包含名为 param 的参数
- param != value : 表示请求必须包含名为 param 的参数,但其值不能为value
-
eg:
- params = {“username”} :表示发送请求必须带上一个名为username的参数,请求路径中必须有参数,如:/hello?username=1
- params = {"!username"}:表示请求不能包含名为username的参数。
- params = {“username=123”},请求中的参数username必须是123。
- params = {"!username=123",“pws=321”},路径中参数之间使用 & 连接。
1.4 header属性
(1)含义
- 规定请求头
(2)作用:
- 限定哪些浏览器可以访问。
-
User-Agent:浏览器信息,可以规定哪些浏览器可以访问,哪些不可以访问
- 谷歌浏览器的User-Agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
- IE浏览器的User-Agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362
- 谷歌浏览器的User-Agent:
-
@RequestMapping(value = "/handle04",headers = {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36"})
public String handle04()
{
return "success";
}
1.5 通配符
RequestMapping的模糊匹配 Ant风格的URL,使用通配符
URL地址中可以以下四个写通配符
?
:能替代任意一个字符*
:能替代任意多个字符(包括0个字符),和一层路径**
: 能替代多层路径
注意:当模糊匹配和精确匹配同时存在时,精确匹配优先。
代码示例:
@Controller
public class RequestMappingTest {
@RequestMapping("/antTest01")
public String antTest01()
{
System.out.println("antTest01");
return "success";
}
/**
* 只要请求路径是 /antTest0 开头,只要最后一个字符不同。
* 模糊和精确多个匹配的情况下,精确匹配优先
* @return
*/
@RequestMapping("/antTest0?")
public String antTest02()
{
System.out.println("antTest02");
return "success";
}
@RequestMapping("/antTest0*")
public String antTest03()
{
System.out.println("antTest03");
return "success";
}
}
二、PathVariable 注解
拿到请求路径中占位符的信息。
路径上可以有占位符:
- 占位符的使用。语法:{变量名}
- 占位符只能占一层路径
eg:
<a href="user/LXY">路径属性值测试</a><br/>
@RequestMapping("/user/{name}")
public String pathVariableTest(@PathVariable("name") String name)
{
System.out.println("路径上的占位符的值为"+name);
return "success";
}
三、REST风格的URL
3.1 REST简介
REST:即 Representational State Transfer。(资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用
-
资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
-
表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
-
状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:(请求方式)
-
GET
用来获取资源 -
POST
用来新建资源 -
PUT
用来更新资源 -
DELETE
用来删除资源。
比如:
发送的请求是 “/book”:
- 如果是以GET方法发送的,就认为是获得book。
- 如果是以DELETE方式发送的,就认为是删除book。
通俗来说:
REST风格是希望以非常简洁的URL地址来发送请求,用请求方式来区分对一个资源的增删改查。
3.2 REST风格URL的使用
(1)我们在JavaWeb中写的URL
-
/getBook?id=1 查询id为1的图书
-
/deleteBook?id=1 删除id为1的图书
(2)REST风格的URL
- 格式: /资源名/资源标识符
- eg:URL为:
/book/1
- GET:查询1号图书
- PUT:更新1号图书
- DELETE:删除1号图书
(3)存在的问题
页面只能发起GET和POST请求,其他方法的请求无法发起
-
解决: Spring提供了REST风格的支持
- SpringMVC有一个Filter,他可以把普通的请求转换为规定形式的请求。
- 在web.xml 中配置Fitter。(HiddenHttpMethodFilter)
- SpringMVC有一个Filter,他可以把普通的请求转换为规定形式的请求。
<!-- 配置filter,将POST请求转换为其他合适的请求方式,如DELETE,PUT-->
<filter>
<filter-name>filter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<!-- 过滤所有请求,只有Servlet中写的是“/”,其他地方的url写的还是“/*”-->
<url-pattern>/*</url-pattern>
</filter-mapping>
- 如何发起其他形式的请求?
- 创建一个
POST
类型的表单 - 表单项(input标签)中携带一个
_method
的参数。 - 这个
_method
的值(value)就是DELETE或者PUT
- 创建一个
<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<html>
<body>
<h2>图书的增删改查,使用REST风格的URL地址</h2>
<a href="book/1">查询图书</a>
<form action="book" method="post">
<input type="submit" value="保存图书">
</form>
<%--Delete请求--%>
<form action="book/1" method="post">
<input name="_method" value="DELETE">
<input type="submit" value="删除图书">
</form>
<%--Put请求--%>
<form action="book/1" method="post">
<input name="_method" value="PUT">
<input type="submit" value="更新图书">
</form>
</body>
</html>
@Controller
public class BookController {
@RequestMapping(value = "/book",method = RequestMethod.POST)
public String addBook()
{
System.out.println("添加图书成功");
return "success";
}
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.DELETE)
public String deleteBook(@PathVariable("bookId") Integer id)
{
System.out.println("已经删除了【 "+id+" 】号图书");
return "success";
}
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.PUT)
public String updateBook(@PathVariable("bookId") Integer id)
{
System.out.println("已经更新了【 "+id+" 】号图书");
return "success";
}
@RequestMapping(value = "/book/{bookId}",method = RequestMethod.GET)
public String getBook(@PathVariable("bookId") Integer id)
{
System.out.println("已经查询到了【 "+id+" 】号图书");
return "success";
}
}
3.3 HiddenHttpMethodFilter源码分析
(1)HiddenHttpMethodFilter的底层实现:
- 确定请求是POST。
- 从请求中拿到“_method”对应的value的值,并转换为大写。
- new出一个新的“HttpServlet对象”即:HttpMethodRequestWrapper,将新的method设置为其新的请求类型。
- 并放行这个新的“HttpServlet”对象。
HiddenHttpMethodFilter部分源码:
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
//拿到_method 对应的值。
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
(2)高版本的Tomcat可能会出现的问题
在上面的程序中可能会出现如下错误:
-
原因:
- 高版本的Tomcat对jsp页面有约束。Tomact 8.0之后
-
解决:
- 在需要跳转到的jsp页面中加上对异常的支持
上面的程序执行完毕之后是要跳转到 success.jsp 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>成功页面</title>
</head>
<body>
<h2>成功!!!</h2>
</body>
</html>
四、参数传入
问:什么叫参数传入?
答:参数传入,即:如何获取前端页面(表单等)提交的参数信息,并在后端处理方法中使用。
4.1 基本类型参数的获取
(1)默认方式
- 默认方式获取请求参数,只需给方法参数上写一个和前度叶面参数名相同的变量,这个变量就自动来接收请求参数的值。
- 有值:则直接获取
- 无值:null
(2)使用注解
-
@RequestParam:获取表单中提交的参数
-
该注解相当于原生Servlet中的:
request.getParameter(key)
-
eg:
public String test01(@RequestParam(value = "username",required = false,defaultValue = "Hello") String name)
{
}
相当于:name=request.getParameter("username);
-
注意:如果直接使用该注解,则默认请求中必须带要获取的参数名,不带就报错。可以使用注解的属性改变。
- value:默认。获取指定key的参数
- required:是否必须指定。布尔类型
- defaultValue:默认值,原本默认是null,现在可以指定默认值
-
注意和 @PathVariable的区别:
@PathVariable
是获取路径中的key对应的值@PathVariable("user")
获取/book/{user}@RequestParam("user")
获取前端提交的参数,例如:form表单中的user参数。
(3)扩展学习两个注解
@RequestHeader 和 @CookieValue
-
@RequestHeader
: 获取请求头中的值。- 相当于:原生Servlet:
request.getHeader("key");
- 如果请求头中没有这个key,会报500错误。
- 该注解也有
value
,required
,defaultValue
属性值,其使用方法和 @RequestParam 中的三个属性一样。
- 相当于:原生Servlet:
-
@CookieValue
:获取某个cookie的值。- 相当于:原生Servlet:
Cookies[] cookies = request.getCookies();
,只不过原生Servlet中获取的是所有的cookie,然后在遍历拿到真正需要的那个cookie。 - 该注解也有三个属性:required,defaultValue,defaultValue。同上。
- 相当于:原生Servlet:
看一段代码来熟悉一下这三个注解
前端页面
<%@page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" isErrorPage="true"%>
<html>
<body>
<h2>测试获取请求参数</h2>
<a href="testParam?username=Tom">获取参数</a><br/>
<a href="testHeader">获取请求头信息</a><br/>
<a href="testCookies">获取Cookies信息</a><br/>
</body>
</html>
controller代码
package nuc;
/**
* 获取请求带来的参数
*/
@Controller
public class RequestParamsController {
/**
*
* @return 成功页面
*/
@RequestMapping(value = "/testParam")
public String testRequest01(@RequestParam(value = "username",required = false,defaultValue = "Hello") String name)
{
System.out.println("请求参数:"+name);
return "success";
}
@RequestMapping(value = "/testHeader")
public String testRequest02(@RequestHeader("User-Agent") String userAgent)
{
System.out.println("请求头信息:"+userAgent);
return "success";
}
@RequestMapping(value = "/testCookies")
public String testRequest03(@CookieValue("JSESSIONID") String jsessionid)
{
System.out.println("Cookies 信息:"+jsessionid);
return "success";
}
}
4.2 自定义类型参数的获取
(1)概念
-
如果我们方法的参数是一个实体类,SpringMVC会自动为这个实体类赋值。
-
将这个实体类中的每一个属性(要求和前端页面提交的参数的名椅子),从request中获取出来,进行封装。
-
还可以级联封装,即:这个实体类中还有另外一个实体类。(在请求中的参数也必须是级联的)
-
注意:这个实体类的属性值的名要和请求中的参数名一致,否则会获取不到这个属性值,即为null。
-
(2)实践
前端页面:
<form action="addBook" method="post">
书名:<input type="text" name="name">
作者:<input type="text" name="author">
价格:<input type="text" name="price">
库存:<input type="text" name="stock">
<hr/>
<input type="text" name="address.province">
<input type="text" name="address.city">//级联属性
<input type="submit">
</form>
Book实体类
public class Book {
private String bookname;
private String author;
private Double price;
private int stock;
private Address address;
}
@RequestMapping("/addBook")
public String addBook(Book book)
{
System.out.println(book);
return "success";
}
这个addBook() 方法中的book,就会和前端页面提交的参数进行一对一绑定。(前提是参数名一致)
五、中文乱码的解决
5.1 请求乱码
1. GET请求
改Tomcat的server.xml文件,在如图位置8080后,加上:URIEncoding=“UTF-8” (我这里并没有修改web.xml文件,大家如果没有遇到过GET请求中文乱码的话,就不必修改此处)
2. POST请求
- 在JavaWeb中的处理方式是,在第一次获取请求参数之前设置:
response.setCharacterEncoding("UTF-8");
- 在SpringMVC中有一个专门的过滤器,它专门用来处理字符乱码。
CharacterEncodingFilter
- 在SpringMVC的配置文件中配置该过滤器。
<!--配置一个过滤字符编码方法的Filter,来处理字符乱码-->
<!--该Filter必须在所有的Filter之前。否则不会有过滤效果-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 设置POST请求编码方式。其实默认就是UTF-8。只不过我们可以使用init-param标签类设置-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--设置响应编码-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 注意:该过滤器要直接写在“前端控制器”的Servlet之后,即:在其他所有的Filter之前,否则会报错!!!
- 原因:因为在JavaWeb中我们就知道,在设置字符编码时,就要在第一次获取参数之前设置。所以,该过滤器就就必须在其他过滤器之前,否则无法起到很好的字符编码过滤,SpringMVC就会发出报错信息。
5.2 响应乱码
对响应乱码的处理非常简单,使用一行代码即可
response.setContentType("text/html;charset=utf-8");
六、使用原生API
这里“使用原生API”的意思就是,在SpringMVC的控制器方法参数列表中使用JavaWeb中的API。即:
- HttpServletResponse:
- HttpServletRequest:
- HttpSession:
还有一些,只不过这三个是常用的,其他的都不常用。这里就不再赘述了
原生API的使用,我们在JavaWeb中已经很熟悉了,这里就直接用一段代码来复习一些。
什么?你竟然没有了解过Servlet?🤦♂️🤦♂️🤦♂️。
也不要紧,先关注一波博主,等我整理完SpringMVC的知识点,就开始上传Servlet的有关东西。传送门【博主】
@RequestMapping("/servletApi")
public String addAPI(HttpSession session, HttpServletRequest request)
{
request.setAttribute("requestParam","我是Request中的参数");
session.setAttribute("sessionParam","我是session中的参数");
return "success";
}
在success.jsp 页面使用EL表达式获取我们放入原生API中的数据(前提是导入JSTL的包)
<html>
<head>
<title>成功页面</title>
</head>
<body>
<h2>成功!!!</h2>
<hr>
Request参数:${requestScope.requestParam}
Session参数:${sessionScope.sessionParam}
</body>
</html>