SpringMVC
是一种基于Java
实现MVC
模型的轻量级Web
框架
- 优点
- 使用简单,开发便捷(相比于
Servlet
) - 灵活性强
- 使用简单,开发便捷(相比于
项目请求响应架构演进:
- 软件三层
- 软件三层 + Web层中的
MVC
(JSP) - 异步调用 + 软件三层 + Web层中的
MVC
(HTML) +Json
前后端分离前端组件化,后台微服务化
- 导入依赖
- 配置
- 少量编码测试
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
3. 导入坐标(SpringMVC+Servlet
)
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- 导入spring-webmvc坐标自动依赖导入spring相关坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
4. 定义处理请求的功能类(UserController
)
//定义表现层控制器bean
@Controller
public class UserController {
//设置映射路径为/save,即外部访问路径
@RequestMapping("/save")
//设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
@ResponseBody
public String save(){ // 处理器 handler,一个控制器中可以包含N个处理器
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
}
注意事项:
对于SpringMVC
而言,Controller
方法返回值默认表示要跳转的页面,没有对应的页面就会报错。
如果不想跳转页面而是响应数据,那么就需要在方法上使用@ResponseBody
注解。
SpringMVC
配置类,加载处理请求的Bean。
//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan("com.cy.controller") // 只扫描web层组件所在的包
public class SpringMvcConfig {
}
6. 加载SpringMVC
配置,并设置SpringMVC
请求拦截的路径
//web容器配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springmvc配置类,产生springmvc容器(本质还是spring容器)
protected WebApplicationContext createServletApplicationContext() {
//初始化WebApplicationContext对象
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
//设置由springmvc控制器处理的请求映射路径,SpringMVC处理拦截哪些请求
protected String[] getServletMappings() {
return new String[]{"/"};
}
//加载spring配置类,创建Spring容器对象
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
1.3 启动服务器初始化过程
- 服务器启动,执行
ServletContainersInitConfig
类,初始化web容器 - 执行
createServletApplicationContext
方法,创建了WebApplicationContext
(SpringMVC
)容器对象那个 - 加载
SpringMVC
的配置类SpringMvcConfig
- 执行组件扫描
@ComponentScan
加载对应的bean
- 加载
UserController
,每个@RequestMapping
的名称对应一个具体的方法 - 执行
getServletMappings
方法,定义所有的请求都通过SpringMVC
- 发送请求
localhost/save
web
容器发现所有请求都经过SpringMVC
,将请求交给SpringMVC
处理- 解析请求路径
/save
- 得知:
/save
匹配执行对应的方法为UserController.save()
- 执行
save()
- 检测到有
@ResponseBody
直接将save()
方法的返回值直接写入响应体
SpringMVC
容器加载Bean
到自己容器中的方式与Spring
一致。
SpringMVC
和Spring
中的Bean
有什么区别?SpringMVC
应该扫描哪些包?
SpringMVC
中bean
(表现层bean)Spring
中的bean
(非表现层的Bean)- 业务
bean
(Service
) - 功能
bean
(DataSource
等) - ….
- 业务
SpringMVC
加载扫描哪些Bean
存放路径:SpringMVC
加载的bean
对应的包均在com.cy.web
包内
Spring
如何实现只扫描自己的bean
- 方式一:
Spring
加载的bean
设定扫描范围为com.cy
,排除掉controller
包内的bean
- 方式二:
Spring
加载的bean
设定扫描范围为精准范围,例如service
包、dao
包等 方式三:不区分Spring
与SpringMVC
的环境,加载到同一个环境中
- 方式一:
通过@ComponentScan
注解的excludeFilters
属性,配出所有标记有@Controller
注解的所有方法。
@Configuration
@ComponentScan(value = "com.itheima",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
- 属性
- excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
- includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)
通过字符串数组的形式,精确指定每个包;
@Configuration
@ComponentScan(value = {"com.itheima.service","com.itheima.dao"})
public class SpringConfig {
}
1.6 创建SpringMvc
容器对象的方式
1.6.1 格式1:
//web容器配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springmvc配置类,产生springmvc容器(本质还是spring容器)
protected WebApplicationContext createServletApplicationContext() {
//初始化WebApplicationContext对象
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
//设置由springmvc控制器处理的请求映射路径,SpringMVC处理拦截哪些请求
protected String[] getServletMappings() {
return new String[]{"/"};
}
//加载spring配置类,创建Spring容器对象
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
1.6.2 简化格式
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class} // 直接返回对应的配置类的Class对象数组
};
protected String[] getServletMappings() {
return new String[]{"/"}; // // 直接返回要处理的请求url匹配方式
}
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class}; // 直接返回对应的配置类的Class对象数组
}
}
2. 请求
2.1 发送get/post请求
2.1.1 GET请求传递普通参数
- 普通参数:url地址传参;
Handler
形参变量名和URL中参数名相同,SpringMVC
就会自动帮我们封装赋值
//普通参数:请求参数与形参名称对应即可完成参数传递
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name ,int age){
System.out.println("普通参数传递 name ==> "+name);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param'}";
}
- 问题:参数如果是中文,接收到的参数出现了中文乱码。
- 原因:
tomcat 8.5
版本之后GET
请求就不再出现中文乱码问题,但是我们使用的是tomcat7
插件,所以会出现GET
请求中文乱码问题。 - 解决:在
pom.xml
添加tomcat7
插件处配置UTF-8
字符集,解决GET
请求中文乱码问题。
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port><!--tomcat端口号-->
<path>/</path> <!--虚拟目录-->
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
2.1.2 POST请求传递普通参数
-
普通参数:
form
表单post
请求传参, -
Handler
形参变量名和表单中表单项name
属性值,SpringMVC
就会自动帮我们封装赋值
//普通参数:请求参数与形参名称对应即可完成参数传递
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name ,int age){
System.out.println("普通参数传递 name ==> "+name);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param'}";
}
问题:我们发现,POST请求传递的参数如果包含中文那么就会出现中文乱码问题,说明我们之前配置的tomcat插件uri路径编解码字符集无法解决POST请求中文乱码问题。那么如何解决呢?
2.1.3 POST请求中文乱码处理在加载
SpringMVC
配置的配置类中指定字符过滤器。
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
// Post请求乱码问题,使用CharacterEncodingFilter处理全局请求乱码
// 模板代码
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
2.2 常见类型参数类型封装
常见的数据类型,我们只要按照SpringMVC
要求,保证名称一致,SpringMVC
就会自动帮我们解析封装赋值。
- 普通参数(基本类型 +
String
) POJO
类型参数- 嵌套
POJO
类型参数 - 数组类型参数
- 集合类型参数
-
普通参数:
-
保证请求参数名与形参名一致,
SpringMVC
便会自动封装 -
当请求参数名与形参变量名不同,使用
@RequestParam
绑定参数关系@RequestParam
中配置的值要和请求参数名一致
-
-
Handler
代码//普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系 @RequestMapping("/commonParamDifferentName") @ResponseBody public String commonParamDifferentName(@RequestParam("name") String userName , int age){ System.out.println("普通参数传递 userName ==> "+userName); System.out.println("普通参数传递 age ==> "+age); return "{'module':'common param different name'}"; }
-
注解说明
- 名称:@
RequestParam
- 类型:形参注解
- 位置:
SpringMVC
控制器方法形参定义前面 - 作用:绑定请求参数与处理器方法形参间的关系
- 参数:
required
:是否为必传参数defaultValue
:参数默认值
- 名称:@
POJO
类型参数
-
POJO
参数:请求参数名与形参中实体对象属性名相同,
SpringMVC
便会自动帮我们封装请求参数到实体对象中 -
Handler
代码public class User { private String name; private int age; //同学们自己添加getter/setter/toString()方法 }
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递 @RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("pojo参数传递 user ==> "+user); return "{'module':'pojo param'}"; }
注意:
请求参数key的名称要和POJO中属性的名称一致,否则无法封装。
POJO
类型参数
POJO
对象中包含POJO
对象
public class User {
private String name;
private int age;
private Address address;
//同学们自己添加getter/setter/toString()方法
}
public class Address {
private String province;
private String city;
private Address address;
}
-
嵌套
POJO
参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套
POJO
属性参数 -
Handler
代码//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递 @RequestMapping("/pojoContainPojoParam") @ResponseBody public String pojoContainPojoParam(User user){ System.out.println("pojo嵌套pojo参数传递 user ==> "+user); return "{'module':'pojo contain pojo param'}"; }
注意:
请求参数key的名称要和
POJO
中属性的名称一致,否则无法封装。
-
数组参数:
多个相同请求参数名与形参中数组变量名一致,
SpringMVC
就会自动把多个相同参数名的值封装到形参位置的数组中。 -
Handler
代码//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中 @RequestMapping("/arrayParam") @ResponseBody public String arrayParam(String[] likes){ System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes)); return "{'module':'array param'}"; }
-
集合保存普通参数:
请求参数名与形参集合对象名相同且请求参数为多个,
@RequestParam
绑定参数关系 -
Handler
代码//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据 @RequestMapping("/listParam") @ResponseBody public String listParam(@RequestParam List<String> likes){ System.out.println("集合参数传递 likes ==> "+ likes); return "{'module':'list param'}"; }
Json
数据参数封装
2.3.1 json
数据参数介绍
json
普通数组(["","","",...])json
对象({key:value,key:value,...}
)json
对象数组([{key:value,...},{key:value,...}]
)
-
设置请求头
低版本
PostMan
发送Json数据,需要设置一个请求头
Content-Type: application/json
-
数据封装提交
JSON
数据需要在请求体中,切勾选Raw,将JSON数据写在下方的文本域中。
json
普通数组
2.3.3.1 代码演示
-
添加
json
数据转换相关坐标<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
-
开启自动转换json数据的支持
@Configuration @ComponentScan("com.itheima.controller") //开启json数据类型自动转换 @EnableWebMvc public class SpringMvcConfig { }
注意事项:
@EnableWebMvc
注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json
数据进行自动类型转换 -
设置发送
json
数据(请求body
中添加json
数据) -
在
Controller
中编写方法接收json参数//集合参数:json格式 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据 //3. Handler形参的参数名任意,因为Json数据没有对应的key @RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List<String> likes){ // 参数名任意 System.out.println("list common(json)参数传递 list ==> "+likes); return "{'module':'list common for json param'}"; }
@EnableWebMvc
- 名称:
@EnableWebMvc
- 类型:配置类注解
- 位置:
SpringMVC
配置类定义上方 - 作用:开启
SpringMVC
多项辅助功能,包括Json
序列化反序列化 - 范例:
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
2.3.3.3 @RequestBody
- 名称:
@RequestBody
- 类型:形参注解
- 位置:
SpringMVC
控制器方法形参定义前面 - 作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器
Handler
方法只能使用一次 - 范例:
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
System.out.println("list common(json)参数传递 list ==> "+likes);
return "{'module':'list common for json param'}";
}
2.3.4 传递json
对象
-
POJO
参数:json
数据与形参对象属性名相同,定义POJO
类型形参即可接收参数 -
Handler
代码//POJO参数:json格式 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc //2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应 @RequestMapping("/pojoParamForJson") @ResponseBody public String pojoParamForJson(@RequestBody User user){ System.out.println("pojo(json)参数传递 user ==> "+user); return "{'module':'pojo for json param'}"; }
json
对象数组
-
POJO
集合参数:json
数组数据与集合泛型属性名相同,定义List
类型形参即可接收参数 -
Handler
代码//集合参数:json格式 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应 @RequestMapping("/listPojoParamForJson") @ResponseBody public String listPojoParamForJson(@RequestBody List<User> list){ System.out.println("list pojo(json)参数传递 list ==> "+list); return "{'module':'list pojo for json param'}"; }
@RequestBody
与@RequestParam
区别
-
区别
@RequestParam
用于接收url
地址传参,表单传参【application/x-www-form-urlencoded
】@
RequestBody
用于接收json
数据【application/json
】 -
应用
后期开发中,发送
json
格式数据为主,@RequestBody
应用较广如果发送非
json
格式数据,选用@RequestParam
接收请求参数
-
格式不同
日期类型数据基于系统不同格式也不尽相同
2088-08-18
2088/08/18
08/18/20882022年5月31日
-
如何解析
当请求的日期的格式不同时,在后台
Handler
参数上添加注解@DateTimeFormat
并通过pattern
属性指定格式,就可以完成解析并自动封装。 -
Handler
代码需要配合
@EnableWebMvc
,才能生效。//日期参数 http://localhost:80/dataParam?date=2088/08/08&date1=2088-08-18&date2=2088/08/28 8:08:08 //使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd @RequestMapping("/dataParam") @ResponseBody public String dataParam(Date date, @DateTimeFormat(pattern="yyyy-MM-dd") Date date1, @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){ System.out.println("参数传递 date ==> "+date); System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1); System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2); return "{'module':'data param'}"; }
@DateTimeFormat
注解介绍
- 名称:@DateTimeFormat
- 类型:形参注解
- 位置:SpringMVC控制器方法形参前面
- 作用:设定日期时间型数据格式
- 属性:pattern:指定日期时间格式字符串
- 其内部依赖Converter接口
public interface Converter<S, T> {
@Nullable
T convert(S var1);
}
- 请求参数年龄数据(String→Integer)
- json数据转对象(json → POJO)
- 日期格式转换(String → Date)
@EnableWebMvc
注解功能二:
- 根据类型匹配对应的类型转换器。
@Controller
public class UserController {
//响应页面/跳转页面
//返回值为String类型,设置返回值为页面名称,即可实现页面跳转
@RequestMapping("/toJumpPage")
public String toJumpPage(){
System.out.println("跳转页面");
return "page.jsp";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>Hello Spring MVC!</h2>
</body>
</html>
4.2 文本数据
//响应文本数据
//返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解
// @RequestMapping("/toText")
@RequestMapping(value = "/toText",produces = "application/json;charset=utf-8") //解决响应乱码问题
@ResponseBody
public String toText(){
System.out.println("返回纯文本数据");
return "response tex你好!!";
}
4.3 json数据
//响应POJO对象
//返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO(){
System.out.println("返回json对象数据");
User user = new User();
user.setName("itcast");
user.setAge(15);
return user;
}
//响应POJO集合对象
//返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList(){
System.out.println("返回json集合数据");
User user1 = new User();
user1.setName("传智播客");
user1.setAge(15);
User user2 = new User();
user2.setName("黑马程序员");
user2.setAge(12);
List<User> userList = new ArrayList<User>();
userList.add(user1);
userList.add(user2);
return userList;
}
注意:需要添加jackson-databind依赖以及在SpringMvcConfig配置类上添加@EnableWebMvc注解
4.4@ResponseBody
和@RequestBody
区别
@ResponseBody
@RequestBody
json
数据
数据格式
把POJO
对象(使用jackson)转成Json
格式字符串,写入响应体 前端请求的
Json
格式字符串,读取并封装成
POJO
对象
相关注解(多看)
@Controller
- 名称:@Controller
- 类型:类注解
- 位置:
SpringMVC
控制器类定义上方 - 作用:设定
SpringMVC
的控制器 - 范例
@Controller
public class UserController {
}
@RequestMapping
- 名称:@
RequestMapping
- 类型:方法、类注解
- 位置:
SpringMVC
控制器(方法)定义上方 - 作用:
- 建立
请求的URL
与处理该请求的处理器(方法)
之间的映射关系 - 限定请求方式、参数、头等内容
- 建立
- 范例
@RequestMapping("/user")
public class UserController{ //控制器
@RequestMapping("/save")
public void save(){ // 处理器handler
System.out.println("user save ...");
}
}
@注意:其实@
RequestMapping
注解还可以写到类上面
ResponseBody
-
名称:@
ResponseBody
-
类型:方法注解
-
位置:
SpringMVC
控制器方法定义上方 -
作用:
-
把
Controller
类中方法(控制器中)的返回值直接写入响应体,而非把返回值作为要跳转的页面名称。一般配合前台的异步请求,局部刷新数据到页面上。
-
-
范例
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
@RequestParam
-
名称:@
RequestParam
-
类型:形参注解
-
位置:
SpringMVC
控制器方法形参定义前面 -
作用:绑定请求参数与处理器方法形参间的关系
可以解决:
- 请求参数名和
Handler
形参对应名称不一样 - 请求参数中多个同名参数,
Handler
形参为集合类型
- 请求参数名和
-
参数:
required
:是否为必传参数defaultValue
:参数默认值
API
(理解)
bstractDispatcherServletInitializer
类
-
AbstractDispatcherServletInitializer
类是SpringMVC
提供的快速初始化Web3.0
容器的抽象类 -
AbstractDispatcherServletInitializer
提供三个接口方法供用户实现createServletApplicationContext()
方法,创建Servlet
容器时,加载SpringMVC
对应的bean并放入WebApplicationContext
对象范围中,而WebApplicationContext
的作用范围为ServletContext
范围,即整个项目范围。
//加载springmvc配置类,产生springmvc容器(本质还是spring容器) protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; }
getServletMappings()
方法,设定SpringMVC
对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC
进行处理。
//设置由springmvc控制器处理的请求映射路径 protected String[] getServletMappings() { return new String[]{"/"}; }
createRootApplicationContext()
方法,如果创建Servlet
容器时需要加载非SpringMVC
对应的bean,使用当前方法进行,使用方式同createServletApplicationContext()
//加载spring配置类 protected WebApplicationContext createRootApplicationContext() { return null; }
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建
web
容器启动类,加载SpringMVC
配置,并设置SpringMVC
请求拦截路径 SpringMVC
核心配置类(设置配置类,扫描controller
包,加载Controller
控制器bean
)
- 多次工作
- 定义处理请求的控制器类
- 定义处理请求的控制器方法,并配置映射路径(@
RequestMapping
)与返回json
数据(@ResponseBody
)