【SSM详细教程】-13-SpringMVC详解
精品专题:
01.《C语言从不挂科到高绩点》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12753294.html?spm=1001.2014.3001.5482
02. 《SpringBoot详细教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12789841.html?spm=1001.2014.3001.5482
03.《SpringBoot电脑商城项目》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12752883.html?spm=1001.2014.3001.5482
04.《VUE3.0 核心教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12769996.html?spm=1001.2014.3001.5482
05. 《SSM详细教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12806942.html?spm=1001.2014.3001.5482
================================
|| 持续分享系列教程,关注一下不迷路 ||
|| 视频教程:墨轩大楼 ||
================================
4. SpringMvc详解
4.1. 请求路径映射
》》 当两个控制器中不同方法绑定了相同的请求路径时,会出现什么状况?
package com.moxuan.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String saveUser(){
System.out.println("user save....");
return "{'msg':'攀哥保存了一个大宝贝儿'}";
}
}
===============================================================================
package com.moxuan.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class AddressController {
@RequestMapping("/save")
@ResponseBody
public String saveAddress(){
System.out.println("Address save....");
return "{'msg':'女朋友给攀哥寄了一个大宝贝儿'}";
}
}
运行起来之后,会出现如下异常:
从异常信息提示可以分析出原因:
UserController有一个saveUser方法,绑定了"/save"。那么它的访问路径为http://localhost/save
AddressController有一个saveAddress方法,也绑定了"/save".访问路径为http://localhost/save
当访问http://localhost/saved的时候,到底是访问UserController还是 AddressController?
》》 开发中潜在的问题
团队多人开发,每人设置不同的请求路径,冲突问题该如何解决?
解决思路:为不同模块设置模块名作为请求路径前置
- 对于Address模块的save,将其访问路径设置http://localhost/address/save
- 对于User模块的save,将其访问路径设置http://localhost/user/save
- 这样在同一个模块中出现命名冲突的情况就比较少了。
修改方法如下:
在控制器上面通过@RequestMapping("命名空间")来设置映射路径,具体代码如下图所示:
package com.moxuan.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String saveUser(){
System.out.println("user save....");
return "{'msg':'攀哥保存了一个大宝贝儿'}";
}
}
====================================================================================
package com.moxuan.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("address")
public class AddressController {
@RequestMapping("/save")
@ResponseBody
public String saveAddress(){
System.out.println("Address save....");
return "{'msg':'女朋友给攀哥寄了一个大宝贝儿'}";
}
}
》》 然后分别用postman测试,结果如下:
发送:http://localhost:8080/user/save
发送:http://localhost:8080/address/save
虽然有乱码,但是可以看到,此时两个请求地址都可以正常访问。这里的乱码问题实际上解决起来比较简单,只需要在请求后面添加 produces = "text/html;charset=UTF-8" 即可,具体代码如下:
@RequestMapping(value = "/save",produces = "text/html;charset=UTF-8")
@ResponseBody
public String saveUser(){
System.out.println("user save....");
return "{'msg':'攀哥保存了一个大宝贝儿'}";
}
再次发送请求时,即可看到正确效果:
》》 @Controller 用法
名称 | @Controller |
类型 | 类注解 |
位置 | SpringMVC控制器类定义上方 |
作用 | 设定SpringMVC的核心控制器bean |
》》@RequestMapping用法
名称 | @RequestMapping |
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value(默认),请求访问路径 |
》》 @ResponseBody用法
名称 | @ResponseBody |
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
4.2. 参数传递
4.2.1. GET 请求传递一个参数
》》 发送请求与参数,格式如下:
http://localhost:8080/user/sayHello?name='攀哥'
》》编写控制器
@RequestMapping(value="/sayHello",produces = "text/html;charset=UTF-8")
@ResponseBody
public String sayHello(String name){
System.out.println(name);
return "{'msg': '"+name+"你好呀'}";
}
》》 运行效果
4.2.2. GET请求传递多个参数
》》 请求地址
http://localhost:8080/user/sayHello?name=攀哥&age=18
》》 编写控制器
@RequestMapping(value="/sayHello",produces = "text/html;charset=UTF-8")
@ResponseBody
public String sayHello(String name,int age){
System.out.println("我叫"+name+",今年"+age+"岁");
return "{'msg': '"+name+"你好呀,你好年轻呀,才"+age+"岁'}";
}
》》测试结果:
由此可以看出,当我们需要传递参数的时候,只需要在控制器的方法中添加与需要传递的参数同名的参数即可。
4.2.3. POST请求传递参数
》》 发送请求的方式,控制器方法不变
4.2.4. POST请求乱码问题
上面的案例中,纵使我们按照解决get请求乱码问题一样添加了produces,也是没有效果,如果是Post请求的话,我们需要在web.xml中配置过滤器,具体配置方法如下:
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
》》重启服务,运行效果如下:
4.3. 请求参数传递
在前面的案例中我们可以使用GET和POST来发送请求和数据,所携带的数据都是比较简单的,接下来,我们在这个基础上研究一下复杂的参数传递。
4.3.1. 普通参数
前面的案例中我们传递的就是普通参数,前面的案例中我们传递时,请求参数的名字和控制器方法中的参数名字相同,可以直接传递,但是如果不相同会出现什么样状况?
@RequestMapping(value="/showCommParam", produces = "text/html;charset=utf-8")
@ResponseBody
public String showParam(String friendName,String sex,Integer age){
System.out.println("我是攀哥的"+sex+"朋友,我叫"+name+"今年"+age+"岁");
return "我是攀哥的"+sex+"朋友,我叫"+name+"今年"+age+"岁";
}
》》发送请求:
》》 请求结果:
》》 解决方案:
可以在控制器方法参数名之前使用@RequestParam 指定这个参数是用来接收哪个请求参数的,比如:
@RequestMapping(value="/showCommParam", produces = "text/html;charset=utf-8")
@ResponseBody
public String showParam(@RequestParam("name") String friendName, String sex, Integer age){
System.out.println("我是攀哥的"+sex+"朋友,我叫"+friendName+"今年"+age+"岁");
return "我是攀哥的"+sex+"朋友,我叫"+friendName+"今年"+age+"岁";
}
》》运行结果:
注意:写上@RequestParam注解框架就不需要自己去解析注入,能提升框架处理性能。
4.3.2. 实体类对象Pojo数据
普通参数类型一般处理的是参数个数比较少的请求,如果参数比较多,后台接收参数的时候就比较复杂,这个时候我们可以考虑使用Pojo类型参数。
注意:Pojo参数,请求参数名与对象的属性名相同,这样请求参数的数据会自动注入道对象属性中,接下来我们看看案例:
》》 POJO实体类Friend
@Data // 自动添加getter和setter方法,toString方法
@NoArgsConstructor // 自动添加无参构造器
@AllArgsConstructor // 自动添加全参构造器
public class Friend {
private String name;
private String sex;
private int age;
}
》》 后台接收参数:
@RequestMapping(value="/showPoJoParam", produces = "text/html;charset=utf-8")
@ResponseBody
public String showPoJoParam(Friend friend){
System.out.println(friend);
return "我是攀哥的"+friend.getSex()+"朋友,我叫"+friend.getName()+"今年"+friend.getAge()+"岁";
}
》》发送请求:
4.3.3. 嵌套POJO类型参数
实际项目中,往往PoJo对象中会嵌套其他PoJo对象,比如:朋友会有地址,地址会有省份,城市等等,具体如下:
》》 地址POJO类Address
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
private String province;
private String city;
}
》》朋友POJO类Friend
@Data // 自动添加getter和setter方法,toString方法
@NoArgsConstructor // 自动添加无参构造器
@AllArgsConstructor // 自动添加全参构造器
public class Friend {
private String name;
private String sex;
private int age;
private Address address;
}
注意:Friend类中我们添加了地址Address对象
》》 控制器接收请求参数
@RequestMapping(value="/showInnerPoJo", produces = "text/html;charset=utf-8")
@ResponseBody
public String showInnerPoJo(Friend friend){
System.out.println(friend);
return "我是攀哥的"+friend.getSex()+"朋友," +
"我叫"+friend.getName()+"今年"+friend.getAge()+"岁," +
"我住在"+friend.getAddress().getProvince()+"省," +
friend.getAddress().getCity()+"市";
}
》》 发送请求:
4.3.4. 数组类型参数
举个简单例子,如果前端需要做用户批量删除或者获取用户的爱好,这些情况下选择的大多是多条数据,那如果遇到这种情况应该如何发送数据和接受数据呢?
注意: 数组参数,请求参数名与控制器方法参数名相同且请求参数为多个,可以定义数组来接收请求参数。
@RequestMapping(value="/showMyLove", produces = "text/html;charset=utf-8")
@ResponseBody
public String showMyLove(String[] loves){
return "给大家介绍一下我的老婆们:"+ Arrays.toString(loves);
}
》》发送请求:
由结果可以看出,同名请求参数可以直接映射到对应的控制器方法的数组参数中。
4.3.5. 集合类型参数
既然数组可以接收多个参数,那么集合是否以可以呢?我们修改一下代码看看:
@RequestMapping(value="/showMyLoveList", produces = "text/html;charset=utf-8")
@ResponseBody
public String showMyLoveList(List<String> loves){
return "给大家介绍一下我的老婆们:"+ loves;
}
》》 发送请求
结果会发现报错了,报错的原因是,SpringMVC将List看作是一个PoJo对象来处理,将其创建一个对象并准备把前端的数据封装到对象中,但是List是一个接口无法创建对象,所以会报错:
解决办法就是在集合前面添加@RequestParam来解决。
@RequestMapping(value="/showMyLoveList", produces = "text/html;charset=utf-8")
@ResponseBody
public String showMyLoveList(@RequestParam List<String> loves){
return "给大家介绍一下我的老婆们:"+ loves;
}
有的同学可能会想,既然List不能直接用,能不能使用ArrayList呢?这个卖点关子,童靴们自己尝试一下吧。关于RequestParam具体使用方法如下:
名称 | @RequestParam |
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定请求参数与处理器方法形参间的关系 |
相关参数 | required:是否为必传参数 defaultValue:参数默认值 |
4.4. JSON数据传输参数
现在流行前后端分离开发模式,而往往在前后端传输数据的时候,会使用JSON。所以接下来我们研究一下当前端发送的是JSON数据时,后端该如何接收?
对于JSON数据类型,我们常见格式有以下几种:
》》JSON 普通数组
["value1","value2","value3",......]
》》JSON 对象
{"key1":"value1","key2":"value2","key3":"value3",.......}
》》JSON 对象数组
[{key1:value1,...},{key2:value2,...}]
SpringMVC 默认的是以jackson来处理JSON的转换,所以我们需要在pom.xml中引入jackson依赖,依赖如下:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.2</version>
</dependency>
在SpringMVC中,解析JSON请求数据需要使用到HttpMessageConverter。
SpringMVC默认提供了多个HttpMessageConverter实现,其中包括MappingJackson2HttpMessageConverter,它可以将JSON数据转换为Java对象。
要使用MappingJackson2HttpMessageConverter,需要在SpringMVC配置文件中进行配置。可以通过以下方式配置:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
4.4.1. 发送普通JSON数组
》》 发送请求
注意发送请求之前一定要按照如下方式在Headers中设置Content-Type,将其设置为application/json
如下图所示依次选择Body,raw, JSON 编写JSON格式的字符串
》》 代码
@RequestMapping(value="/showJsonParam", produces = "text/html;charset=utf-8")
@ResponseBody
public String showJsonParam(@RequestBody String loves){
// 解析JSON格式的字符串
List<String > mys = (List<String>) JSON.parse(loves);
for (String myLove:mys){
System.out.println(myLove);
}
System.out.println(mys);
return loves.toString();
}
》》运行结果:
4.4.2. 传输JSON对象数据
》》 发送如下请求
》》 控制器代码如下:
@RequestMapping(value="/showJsonFriend", produces = "text/html;charset=utf-8")
@ResponseBody
public String showJsonFriend(@RequestBody Friend friend){
System.out.println(friend);
return friend.toString();
}
》》运行结果:
从效果可以看出,当json格式中的数据key和对象中的属性同名时,json数据会自动注入到对象的属性中去。
4.4.3. JSON对象数组
》》发送请求如下图所示:
》》控制器代码如下:
@RequestMapping(value="/showJsonManyFriend", produces = "text/html;charset=utf-8")
@ResponseBody
public String showJsonManyFriend(@RequestBody List<Friend> friends){
for (Friend friend:friends){
System.out.println(friend);
}
return friends.toString();
}
》》运行结果:
4.4.4. @RequestBody与@RequestParam区别
- 区别
-
- @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
- @RequestBody用于接收json数据【application/json】
- 应用
-
- 后期开发中,发送json格式数据为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数