SpringMVC-入门使用
- 理解SpringMVC相关概念
- 完成SpringMVC的入门案例
- 学会使用PostMan工具发送请求和数据
- 掌握SpringMVC如何接收请求数据和响应结果
- 掌握RESTful风格及使用
- 完成基于RESTful的案例编写
# SpringMVC简介
从SpringMVC名字上就可以看出其与Spring应该是有关系的,SpringMVC隶属于Spring,是Spring技术中的一部分,那么SpringMVC到底可以用来做什么呢?
之前在JavaWeb课程的学习阶段,我们学习了Servlet,实际上SpringMVC和Servlet技术作用类似,均属于Web层或者说表现层的开发技术。
那么既然已经有了Servlet技术为什么还需要花精力学习SpringMVC技术?要回答这个问题,我们就需要弄明白SpringMVC与Servlet相比,有哪些优势?
框架我们都知道是用来简化开发的,所以SpringMVC与Servlet相比,开发起来肯定要更简单便捷,可以用更少的代码完成表现层代码的开发,下面我们可以通过一个例子来体验一下。
将资料中的springmvc-01-demo项目导入到Idea中,打开后模块目录结构如下:
UserSaveServlet
:使用Servlet开发的用户新增模块
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/user/save")
public class UserSaveServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 接收请求参数
String name = req.getParameter("name");
System.out.println("servlet save name ==> " + name);
// 2. 响应数据
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write("{'module': 'servlet save'}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
UserUpdateServlet
: 使用Servlet开发的用户修改模块
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/user/update")
public class UserUpdateServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 接收请求参数
String name = req.getParameter("name");
System.out.println("servlet update name ==> " + name);
// 2. 响应数据
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write("{'module': 'servlet update'}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
UserDeleteServlet
:使用Servlet开发的用户删除模块
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/user/delete")
public class UserDeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 接收请求参数
String name = req.getParameter("name");
System.out.println("servlet delete name ==> " + name);
// 2. 响应数据
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{'module': 'servlet delete'}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
UserSelectDelete
: 使用Servlet开发的用户查询模块
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/user/select")
public class UserSelectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 接收请求参数
String name = req.getParameter("name");
System.out.println("servlet select name ==> " + name);
// 2. 响应数据
resp.setContentType("application/json;charset=utf-8");
resp.getWriter().write("{'module': 'servlet select'}");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
启动项目,测试结果如下:
上面就是通过Servlet实现的用户模块的增删改查功能。如果使用SpringMVC技术来开发相同的功能,代码是什么样子的?
打开com.itheima.springmvc
包下的UserController
,内容如下:
package com.itheima.springmvc;
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 save(String name) {
System.out.println("springmvc save name ==> " + name);
return "{'module': 'springmvc save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete(String name) {
System.out.println("springmvc delete name ==> " + name);
return "{'module': 'springmvc delete'}";
}
@RequestMapping("/update")
@ResponseBody
public String update(String name) {
System.out.println("springmvc update name ==> " + name);
return "{'module': 'springmvc update'}";
}
@RequestMapping("/select")
public String select(String name) {
System.out.println("springmvc select name ==> " + name);
return "{'module': 'springmvc select'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
启动项目,测试结果如下:
通过两种技术对相同功能的实现,会发现功能效果是一样的,但是对比编写的代码会发现SpringMVC技术更简单、更高效。
SpringMVC使用来替换Servlet的,所以Servlet能实现的,SpringMVC也能实现,主要学习的内容有以下几个方面:
- SpringMVC简介
- 请求与响应
- REST风格
- SSM整合(基于注解)
- 拦截器
SpringMVC简介:主要是简单认识下SpringMVC。
请求与响应:SpringMVC是替换Servlet,其是作用于Web层的框架,所以其主要的作用就是用来接收前端发过来的请求和数据然后经过处理并将处理的结果响应给前端,所以如何处理请求和响应是SpringMVC中非常重要的一块内容。
RESTful风格:是一种软件架构风格,可以降低开发的复杂性,提高系统的可伸缩性,在以后的开发中非常常用和重要。
SSM整合:是将我们学习的SpringMVC + Spring + MyBatis整合在一起来完成业务的开发,是对我们所学习的这三个框架的一个综合应用。
拦截器:是SpringMVC中的一个小知识点。
对于SpringMVC的学习,最终要达成的目标:
- 掌握基于SpringMVC获取请求参数和响应JSON数据操作
- 熟练应用基于REST风格的请求路径设置与参数传递
- 能够根据实际业务建立前后端开发通信协议并进行实现
- 基于SSM整合技术开发任意业务模块功能
下面我们就进入SpringMVC这一章内容的学习,在这一章中,我们主要学习如下内容:
- SpringMVC概述
- 入门案例
- 入门案例工作流程分析
- Controller加载控制
- PostMan
本章的核心内容为:入门案例和入门案例工作流程分析。
# SpringMVC概述
上面我们已经简单了解了SpringMVC相关的知识,下面我么通过一张图在详细介绍下SringMVC这门技术。
Web程序的工作流程:三层架构
- Web程序通过浏览器访问前端页面,发送异步请求到后端服务器
- 后台服务器采用三层架构进行业务功能开发
- 表现层负责接收请求和数据,然后将数据转交给业务层
- 业务层负责调用数据层完成数据的增删改查,并将结果返给表现层
- 表现层将数据转换成JSON格式数据返回给前端页面
表现层和数据层的技术选型:
- 数据层采用MyBatis框架
- 表现层采用SpringMVC框架,SpringMVC主要负责的内容有:
- controller如何接收请求数据?
- 如何将请求和数据转发给业务层?
- 如何将响应数据转换成JSON数据返回给前端页面?
小结
- SpringMVC是一种基于MVC架构以Java实现的轻量级Web框架
- SpringMVC具有以下优点
- 使用简单、开发便捷(相比于Servlet)
- 灵活性强
# SpringMVC入门案例
SpringMVC是一个Web框架,用来替换之前我们使用的Servlet,我们先来回顾下之前Servlet的开发流程:
- 浏览器发送请求到Tomcat服务器
- Tomcat服务器接收请求后,根据请求路径来匹配找到对应的Servlet,并将请求交给对应的Servlet来处理
因此对于Servlet来说,主要完成的就是Servlet类的开发以及对应路径的配置。
那么对于SpringMVC程序的开发流程又是什么样的呢?
- 浏览器发送请求到Tomcat服务器
- Tomcat服务器接收到请求后,将请求交给SpringMVC中的
DispatcherServlet
*(前端控制器)*来处理请求 DispatcherServlet
不真正处理请求,而是按照对应的规则将请求分发到对应的Bean
对象Bean
对象是根据业务需求由我们开发者编写的,用于处理不同的请求,每个Bean
中可以处理一个或多个不同的请求URLDispatcherServlet
和Bean
对象都需要交给Spring容器来进行管理。
综上所述,使用SpringMVC开发Web程序,需要开发者编写的内容有:
Bean
对象的编写- 请求URL和
Bean
对象对应关系的配置 - 构建Spring容器,将
DispatcherServlet
和Bean
对象交给Spring容器管理 - 配置Tomcat服务器,使其能够识别Spring容器,并将请求交给容器中的
DispatcherServlet
来分发请求
具体的实现步骤如下:
- 创建Web工程(Maven结构)并在工程的pom.xml中添加SpringMVC和Servlet的依赖坐标
- 创建SpringMVC控制器类
controller
(等同于Servlet功能) - 初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的
bean
- 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
# 案例需求分析
# 步骤一:案例制作(环境准备)
打开Idea,创建一个新的Web模块,并初始化目录结构:
完成上面步骤后,按照下图所示补全缺失的目录结构:
将pom.xml文件中多余的内容删除掉,在添加SpringMVC和Servlet所需要的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>springmvc-02-quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
修改完pom.xml文件后,最好通过Maven Projects刷新下模块
说明: Servlet的坐标为什么要添加`
scope
是Maven中Jar包依赖的作用范围的描述- 如果不设置默认是
compile
在编译、测试、运行时均有效 - 如果运行时有效的话就会和Tomcat中的servlet-api包发生冲突,导致启动时报错
provided
代表的是该包只在编译和测试的时候有效,运行的时候无效直接使用tomcat中的即可,这样就避免了冲突。
# 步骤2:创建控制器类
package com.itheima.controller;
// 2. 制作控制器类,等同于Servlet
// 2.1 必须是一个Spring管理的Bean
// 2.2 定义具体处理请求的方法
// 2.3 设置当前方法的访问路径
// 2.4 设置响应结果为JSON数据
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 save() {
System.out.println("user save ...");
return "{'module': 'springmvc'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 步骤3:创建配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 3. 定义配置类加载Controller对应的Bean
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
# 步骤4:创建Tomcat的Servlet容器配置类
package com.itheima.config;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
// 4. 定义Servlet容器的配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
// 加载SpringMVC配置
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 设置Tomcat接收的请求哪些由SpringMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// 设置Spring相关配置
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 步骤5:配置Tomcat环境
# 步骤6:启动运行项目
# 步骤7:浏览器访问
至此,SpringMVC的入门案例就全部完成了。
# 知识点1:@Controller
名称 | @Controller |
---|---|
类型 | 类注解 |
位置 | SpringMVC控制器类定义上方 |
作用 | 设定SpringMVC的核心控制器Bean |
# 知识点2:@RequestMapping
名称 | @RequestMapping |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value (默认),请求访问路径 |
# 知识点3:@ResponseBody
名称 | @ResponseBody |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
# 入门案例总结
SpringMVC入门程序开发总结(1 + N)
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建Web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描
controller
包,加载Controller控制器bean
)
- 多次工作
定义处理请求的控制器类
定义处理请求的控制器方法,并配置映射路径(
@RequestMapping
)与返回JSON数据(@ResponseBody
)多次工作我们可以通过实现一个用户的删除功能来体验一下
controller
类中编写删除方法
package com.itheima.controller; // 2. 制作控制器类,等同于Servlet // 2.1 必须是一个Spring管理的Bean // 2.2 定义具体处理请求的方法 // 2.3 设置当前方法的访问路径 // 2.4 设置响应结果为JSON数据 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 save() { System.out.println("user save ..."); return "{'module': 'springmvc'}"; } @RequestMapping("/delete") @ResponseBody public String delete() { System.out.println("user delete ..."); return "{'module': 'springmvc delete'}"; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29- 重启服务器,查看效果
针对本案例中出现的*Tomcat*的*Servlet*容器配置作如下总结
AbstractDispatcherServletInitializer
类是SpringMVC提供的快速初始化Web3.0容器的抽象类AbstractDispatcherServletInitializer
提供三个接口方法供开发者实现createRootApplicationContext()
方法,如果创建Servlet容器时需要加载非SpringMVC对应的Bean
,使用该方法进行,使用方式同createServletApplicationContext()
createServletApplicationContext()
方法,创建Servlet容器时,加载SpringMVC对应的Bean
并放入WebApplicationContext
对象范围中,而WebApplicationContext
的作用范围为ServletContext
范围,即整个Web容器的范围getServletMappings()
方法,设定SpringMVC对应的请求映射路径,设置为/
表示拦截所有请求,任意请求都将转入到SpringMVC进行处理createServletApplicationContext
用来加载SpringMVC环境createRootApplicationContext
用来加载Spring环境
# 入门案例工作流程分析
为了更好的使用SpringMVC,我们将SpringMVC的使用过程总共分为两个阶段来分析,分别是启动服务器初始化过程和单词请求过程。
# 启动服务器初始化过程
服务器启动,执行
ServletContainersInitConfig
类,初始化Web容器执行
createServletApplicationContext
方法,创建WebApplicationContext
对象- 该方法加载SpringMVC的配置类
SpringMvcConfig
来初始化SpringMVC容器
- 该方法加载SpringMVC的配置类
加载
SpringMvcConfig
配置类// 3. 定义配置类加载Controller对应的Bean @Configuration @ComponentScan("com.itheima.controller") public class SpringMvcConfig { }
1
2
3
4
5执行
@ComponentScan
加载对应的Bean
- 扫描指定包下所有类上的注解,如
UserController
类上的@Controller
注解
- 扫描指定包下所有类上的注解,如
加载
UserController
,每个@RequestMapping
的名称对应一个具体的方法@RequestMapping("/save") @ResponseBody public String save() { System.out.println("user save ..."); return "{'module': 'springmvc'}"; }
1
2
3
4
5
6- 此时就建立了
/save
和save()
方法的对应关系
- 此时就建立了
执行
getServletMappings
方法,定义所有的请求都通过SpringMVC// 设置Tomcat接收的请求哪些由SpringMVC处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; }
1
2
3
4
5/
代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求
# 单次请求过程
- 发送请求
localhost/save
- Web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
- 因为该路径符合上面第六步设置的拦截路径,所以该请求会交给SpringMVC处理
- 解析请求路径
/save
- 由
/save
匹配对应的方法save()
执行- 上面的第五步已经将请求路径和方法建立了对应关系,通过
/save
就能找到对应的save
方法
- 上面的第五步已经将请求路径和方法建立了对应关系,通过
- 执行
save()
方法 - 检测到有
@ResponseBody
直接将save()
方法的返回值作为响应体返回给请求方
# bean加载控制
# Bean加载控制-问题引入
上一小节,入门案例的内容已经做完了,在入门案例中我们创建一个SpringMvcConfig
配置类,再回想之前咱们学习Spring时也创建过一个配置类SpringConfig
。这两个配置类都需要加载资源,那么SpringMVC和Spring分别都需要加载哪些内容呢?
首先来看下咱们目前的项目目录结构:
- config目录存入的是配置类,曾经在这个目录中出现过的配置类有:
ServletContainersInitConfig
SpringConfig
SpringMvcConfig
JdbcConfig
MyBatisConfig
- controller目录存放的是SpringMVC的
controller
类 - service目录存放的是
service
接口和实现类 - dao目录存放的是
dao/Mapper
接口
controller、service和dao这些类都需要被容器管理成Bean
对象,那么到底是由SpringMVC加载还是Spring加载呢?
- SpringMVC加载其相关的
Bean
(表现层Bean
),也就是controller包下的类 - Spring控制的
Bean
- 业务
bean
(Service) - 功能
bean
(DataSource
、SqlSessionFactoryBean
、MapperScannerConfigurer
等)
- 业务
理清楚SpringMVC和Spring谁管理哪些bean
以后,接下来要解决的问题是如何让Spring和SpringMVC分开加载各自所需的内容。
在SpringMVC的配置类SpringMvcConfig
中使用注解@ComponentScan
,我们只需要将其扫描范围设置到controller
即可,如
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 3. 定义配置类加载Controller对应的Bean
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
在Spring的配置类SpringConfig
中使用注解@ComponentScan
,这个注解扫描的包为com.itheima
,这个扫描的范围中其实已经包含了controller
,如:
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
2
3
4
5
6
7
8
9
从包结构来看,Spring已经把SpringMVC的controller
包下的类也给扫描到了所以针对这个问题该如何解决,就是接下来我们要学习的内容:
实际上,我们接下来要解决的问题就是因为功能不同,如何避免Spring错误加载到SpringMVC的Bean
?
# Bean加载控制-思路分析
针对上面的问题,解决方案也比较简单,就是:
- 加载Spring控制的
bean
的时候排除掉SpringMVC控制的bean
,有两种方式解决:- 方式一:Spring加载的
bean
设定扫描范围为com.itheima
,然后排除掉controller
包中的bean
- 方式二:Spring加载的
bean
设定扫描范围为精准范围,例如:service
包,dao
包等
- 方式一:Spring加载的
# Bean加载控制-环境准备
- 创建一个Web的Maven项目
- pom.xml添加Spring依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>springmvc-03-bean-load</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- 创建对应的配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 3. 定义配置类加载Controller对应的Bean
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
2
3
4
5
6
7
8
9
package com.itheima.config;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
// 4. 定义Servlet容器的配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
// 加载SpringMVC配置
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 设置Tomcat接收的请求哪些由SpringMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// 设置Spring相关配置
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 编写Controller、Service、Dao、Domain类
package com.itheima.controller;
// 2. 制作控制器类,等同于Servlet
// 2.1 必须是一个Spring管理的Bean
// 2.2 定义具体处理请求的方法
// 2.3 设置当前方法的访问路径
// 2.4 设置响应结果为JSON数据
import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("user save ...");
userService.save(new User());
return "{'module': 'springmvc'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.itheima.service;
import com.itheima.domain.User;
public interface UserService {
void save(User user);
}
2
3
4
5
6
7
package com.itheima.service.impl;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void save(User user) {
System.out.println("user service ...");
userDao.save(user);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.itheima.dao;
import com.itheima.domain.User;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
void save(User user);
}
2
3
4
5
6
7
8
9
10
11
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
System.out.println("user dao save ...");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
package com.itheima.domain;
public class User {
}
2
3
4
最终创建好的项目结构如下:
# 设置Bean加载控制
方式一:修改Spring配置类SpringConfig
,设定扫描范围为精准范围
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({"com.itheima.service", "com.itheima.dao"})
public class SpringConfig {
}
2
3
4
5
6
7
8
9
说明
上述只是通过例子说明可以精确指定让Spring扫描对应的包结构,真正在做开发的时候,因为Dao最终是交给MapperScannerConfiguer
对象来进行扫描处理的,我们只需要将其扫描到service
包即可
方式二:修改Spring配置类SpringConfig
,设定扫描范围为com.itheima
,排除掉controller
包中的Bean
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
@Configuration
// 方式一
//@ComponentScan({"com.itheima.service", "com.itheima.dao"})
// 设置spring配置类加载bean时的过滤规则,当前要求排除掉表现层对应的bean
// excludeFilters属性:设置扫描加载bean时,排除的过滤规则
// type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除
// classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
@ComponentScan(value = {"com.itheima"}, excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
))
public class SpringConfig {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
excludeFilters
属性:设置扫描加载bean
时,排除的过滤规则type
属性:设置排除规则,当前使用按照bean
定义时的注解类型进行排除ANNOTATION
: 按照注解排除ASSIGNABLE_TYPE
: 按照指定的类型过滤ASPECTJ
: 按照Aspectj
表达式排除,基本上不用REGEX
: 按照正则表达式排除CUSTOM
: 按照自定义规则排除
大家只要了解第一种ANNOTATION
即可
classes
属性:设置排除的具体注解类,当前设置排除@Controller
定义的bean
如何测试?
package com.itheima;
import com.itheima.config.SpringConfig;
import com.itheima.controller.UserController;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
UserController userController = ctx.getBean(UserController.class);
System.out.println(userController);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
如果被排除了,该测试方法执行时回报bean
未定义的错误
注意
测试的时候,将SpringMvcConfig
配置类上的@ComponentScan
注解注释掉,否则不会报预期的错误。
原因:
- Spring配置类扫描的包是
com.itheima
- SpringMVC的配置类,
SpringMvcConfig
上有一个@Configuration
注解,也会被Spring扫描到 SpringMvcConfig
上又有@ComponentScan
,把controller
包下的类又扫描了进来- 所以如果不注释掉
SpringMvcConfig
类中的@ComponentScan
,Spring配置类将Controller
排除,但是因为扫描到SpringMVC配置类,又将其加载回来,演示的效果就出不来。 - 解决方案:把SpringMVC的配置类移除Spring配置类的扫描范围即可。
最后一个问题:有了Spring配置类,如果希望在Tomcat服务器启动时将其加载,就需要修改ServletContainersInitConfig
配置类
package com.itheima.config;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
// 4. 定义Servlet容器的配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createServletApplicationContext() {
// 加载SpringMVC配置
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 设置Tomcat接收的请求哪些由SpringMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// 设置Spring相关配置
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
对于上述的配置方式,Spring还提供了一种更简单的配置方式,可以不用创建AnnotationConfigWebApplicationContext
对象,更不用手动register
对应的配置类,下面的代码就是具体的实现。
package com.itheima.config;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
//// 4. 定义Servlet容器的配置类
//public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//
// @Override
// protected WebApplicationContext createServletApplicationContext() {
// // 加载SpringMVC配置
// AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// // 加载指定配置类
// ctx.register(SpringMvcConfig.class);
// return ctx;
// }
//
// // 设置Tomcat接收的请求哪些由SpringMVC处理
// @Override
// protected String[] getServletMappings() {
// return new String[]{"/"};
// }
//
// // 设置Spring相关配置
// @Override
// protected WebApplicationContext createRootApplicationContext() {
// AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// ctx.register(SpringConfig.class);
// return ctx;
// }
//
//}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 知识点4:@ComponentScan
名称 | @ComponentScan |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置Spring配置类扫描路径,用于加载使用注解格式定义的bean |
相关属性 | excludeFilters : 排除扫描路径中加载的bean ,需要指定类别type 和具体项classes includeFilters : 加载指定的bean ,需要指定类别type 和具体项classes |
# PostMan工具的使用
# PostMan简介
接口编写完后,如果需要测试,我们可以打开浏览器输入地址发送请求即可,但是如果我们发送的是GET请求可以直接使用浏览器,但是如果是POST请求呢?
如果发送的是POST请求,我们就需要准备页面并且在页面上编写form
表单,测试起来相当麻烦,因此我们就需要借助一些第三方工具,如PostMan。
- PostMan是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件
- 作用:常用于接口测试
- 特征: 简单实用
# PostMan安装
双击课程资料中的PostMan安装文件,即可自动安装,安装完成后,如果需要注册,可以按照提示进行注册,如果底部有跳过注册的链接也可以点击跳过注册。
看到如下界面,说明已经安装成功
# PostMan使用
# 创建WorkSpace工作空间
# 使用PostMan发送请求
# 保存当前请求
注意
第一次请求需要创建一个新的目录,后面就不需要创建目录,直接保存到已经创建好的目录即可。
# 请求与响应
上面我们已经完成了入门案例相关知识的学习,接下来我们就需要针对SpringMVC相关的知识点进行系统的学习,之前我们提到过,SpringMVC是Web层的框架,主要的作用是接收请求、响应结果,因此这一章节我们学习SpringMVC的重点内容如下:
- 请求映射路径
- 请求参数
- 日期类型参数传递
- 响应JSON数据
# 设置请求映射路径
# 请求映射路径-环境准备
- 创建一个基于Maven骨架的Web项目
- pom.xml添加SpringMVC、Servlet依赖坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>springmvc-04-request-mapping</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 创建对应的配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
package com.itheima.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 编写
BookController
和UserController
package com.itheima.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 save() {
System.out.println("user save ...");
return "{'module': 'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete() {
System.out.println("user delete ...");
return "{'module': 'user delete'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.itheima.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class BookController {
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("book save ...");
return "{'module': 'book save'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
最终创建好的项目结构如下
环境准备好后,启动Tomcat服务器,后台会报如下错误:
从错误信息可以看出:
UserController
中有save
方法,访问路径为http://localhost/save
BookController
中有save
方法,访问路径为http://localhost/save
- 当访问
http://localhost/save
的时候,那么到底访问的是UserController
还是BookController
?
# 请求映射路径-问题分析
当企业中一个团队多人开发时,没人设置不同的请求路径,冲突问题该如何解决?
解决思路:为不同的模块设置模块名作为请求路径前置
对于Book模块的save
,将其访问路径设置为http://localhost/book/save
对于User模块的save
,将其访问路径设置为http://localhost/user/save
这样在同一个项目中出现命名冲突的情况就比较少了。
# 请求映射路径-配置
- 修改Controller
package com.itheima.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("/user/save")
@ResponseBody
public String save() {
System.out.println("user save ...");
return "{'module': 'user save'}";
}
@RequestMapping("/user/delete")
@ResponseBody
public String delete() {
System.out.println("user delete ...");
return "{'module': 'user delete'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.itheima.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class BookController {
@RequestMapping("/book/save")
@ResponseBody
public String save() {
System.out.println("book save ...");
return "{'module': 'book save'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
提示
按照上面的代码,问题是可以得到解决,但是每个方法前面都需要进行修改,而且还会出现很多重复代码,如果/user
后期发生变化,所有的方法都需要修改,不易扩展。
- 优化路径配置
优化方案:
package com.itheima.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 save() {
System.out.println("user save ...");
return "{'module': 'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete() {
System.out.println("user delete ...");
return "{'module': 'user delete'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.itheima.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("book save ...");
return "{'module': 'book save'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
注意
- 当类上和方法上都添加了
@RequestMapping
注解,前端发送请求的时候,要和两个注解的value
值相加匹配才能访问到 @RequestMapping
注解value
属性前加不加/
都可以
PostMan 扩展
对于PostMan如果觉得字体较小,可以使用Ctrl +
组合键调大字体,Ctrl -
调小字体
# 请求参数
请求路径设置好,只要确保页面发送请求地址和后台Controller
类中配置的路径一致,就可以接收到前端的请求,随之而来就有一个问题,接收到请求后,如何接收页面传递的参数?
关于请求参数的传递与接收是和请求方式有关的,当前比较常见的两种请求方式为:
- GET
- POST
针对与不同的请求前端如何发送,后端如何接收?下面我们就来回答一下这个问题
# 请求参数-环境准备
- 创建一个基于Maven项目骨架的Web项目
- pom.xml中引入SpringMVC、Servlet的依赖坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>springmvc-05-request-param</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 创建对应的配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
package com.itheima.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 编写
UserController
package com.itheima.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("/commonParam")
@ResponseBody
public String commonParam() {
return "{'module': 'commonParam'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 编写模型类,
User
和Address
package com.itheima.domain;
public class User {
private String name;
private int age;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.itheima.domain;
public class Address {
private String province;
private String city;
public Address() {
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
最终创建好的项目结构如下:
# 请求参数-参数传递
GET发送单个参数:
// 1. Get发送单个参数
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name) {
System.out.println("普通参数传递 name ==> " + name);
return "{'module': 'commonParam'}";
}
2
3
4
5
6
7
GET发送多个参数:
// 1. Get发送单个参数
// 2. Get发送多个参数
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name, int age) {
System.out.println("普通参数传递 name ==> " + name);
System.out.println("普通参数传递 age ==> " + age);
return "{'module': 'commonParam'}";
}
2
3
4
5
6
7
8
9
POST发送参数:
和GET代码一致,不用作任何修改
// 1. Get发送单个参数
// 2. Get发送多个参数
// 3. Post传送多个参数
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name, int age) {
System.out.println("普通参数传递 name ==> " + name);
System.out.println("普通参数传递 age ==> " + age);
return "{'module': 'commonParam'}";
}
2
3
4
5
6
7
8
9
10
POST请求中文乱码:
控制台打印,发现中文乱码
解决方案:配置过滤器
package com.itheima.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 解决中文乱码问题,配置过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
CharacterEncodingFilter
是在spring-web包中,所以需要事先导入对应的依赖坐标。
# 五种类型参数传递
前面我们已经能够使用GET或POST来发送请求数据,其中携带的数据都是比较简单的数据,接下来在这个基础上,我们来研究一些比较复杂的参数传递,常见的有:
- 普通参数
- POJO类型参数
- 嵌套POJO类型参数
- 数组类型参数
- 集合类型参数
这些参数该如何发送和接收,我们来一个个学习下。
# 普通参数
- 普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数
那么如果形参变量名与地址传入的参数名不一致应该如何解决呢?比如:
http://localhost/commonParamDifferentName?name=张三&age=18
后台接收参数代码:
// http://localhost/commonParamDifferentName?name=张三&age=17
// url传入参数名和形参变量名不一致
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(String userName, int age) {
System.out.println("普通参数传递 userName ==> "+userName);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param different name'}";
}
2
3
4
5
6
7
8
9
因为前端给的是name
,后台接收使用的是userName
,两个名称对应不上,导致接收数据失败:
解决方案:使用@RequestParam
注解
// http://localhost/commonParamDifferentName?name=张三&age=17
// url传入参数名和形参变量名不一致
// @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'}";
}
2
3
4
5
6
7
8
9
10
注意
使用@RequestParam
注解,框架就不需要自己去解析注入,可以提升处理性能。
# POJO数据类型
简单数据类型一般处理的是参数个数比较少的请求,如果参数比较多,那么后台接收参数的时候就比较复杂,这个时候可以考虑使用POJO数据类型
POJO参数:请求参数名与形参对象属性相同,定义POJO类型形参即可接收参数
此时需要使用前面准备好的POJO类,先来看下User
package com.itheima.domain;
public class User {
private String name;
private int age;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user) {
System.out.println("pojo参数传递 user ==> "+user);
return "{'module':'pojo param'}";
}
2
3
4
5
6
7
注意
- POJO参数接收,前端GET和POST请求方式不变
- 请求参数
key
的名称要和*POJO中属性的名称一致,否则无法封装。
# 嵌套PJO类型参数
如果POJO对象中嵌套了其他的POJO类,如:
package com.itheima.domain;
public class User {
private String name;
private int age;
private Address address;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
// POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user) {
System.out.println("pojo参数传递 user ==> "+user);
return "{'module':'pojo param'}";
}
2
3
4
5
6
7
注意
请求参数key
的名称要和POJO中属性的名称一致,否则无法封装。
# 数组类型参数
如果前端需要获取用户的爱好,爱好绝大多数情况是多个,也即是复选框,那么针对这种情况如何发送请求数据和接收数据呢?
数组参数:请求参数名与形参变量名或形参对象属性名相同且传入请求参数为多个,定义数组类型即可接收参数。
// 数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes) {
System.out.println("数组参数传递 likes ==> " + Arrays.toString(likes));
return "{'module': 'array param'}";
}
2
3
4
5
6
7
# 集合类型参数
数组可以接收多个参数值,那么集合是否也可以实现同样的功能?
// 集合参数:同名参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(List<String> likes) {
System.out.println("集合参数传递 likes ==> " + likes);
return "{'module': 'list param'}";
}
2
3
4
5
6
7
按照如下发送请求后,后台接收数据报错:
错误的原因是:SpringMVC将List
看做是一个POJO对象来处理,将其创建一个对象并准备把前端的数据封装到对象中,但是List
是一个接扣无法创建对象,因此报错。
解决方案:使用@RequestParam
注解
// 集合参数:同名参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes) {
System.out.println("集合参数传递 likes ==> " + likes);
return "{'module': 'list param'}";
}
2
3
4
5
6
7
小结
- 集合保存普通参数:请求参数名与形参集合对象名仙童且请求参数为多个,
@RequestParam
绑定参数关系 - 对于简单数据类型使用数组会比集合更简单
# 知识点5:@RequestParam
名称 | @RequestParam |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定请求参数与处理器方法形参间的关系 |
相关参数 | required : 是否为必传参数 defaultValue : 参数默认值 |
# JSON数据传输参数
在JavaWeb阶段我们提到过,现在比较流行的开发方式为异步调用,前后台以异步方式进行交换,传输的数据使用的是JSON,所以前端如果发送的是JSON数据,那么后端该如何便捷的接收呢?
JSON数据类型:
- JSON普通数组(
["value1", "value2", "value3", ...]
) - JSON对象(
{key1: value1, key2: value2, ...}
) - JSON对象数组(
[{key1: value1, ...}, {key2: value2, ...}]
)
下面我们就逐个看下针对上述数据,前端如何发送请求携带参数,后端接收请求数据?
在JavaWeb阶段我们已经讲过JSON这一数据结构,并且完成了JSON -> JavaBean和JavaBean -> JSON之间的转换,当时我们使用的是阿里巴巴开发的一款叫fastjson的工具,那么SpringMVC如果想快速的处理JSON数据,也需要引入一个类似的工具,SpringMVC默认使用的是jackson来处理JSON,因此我们需要先引入jackson。
# JSON普通数组
步骤1:在pom.xml文件中引入jackson依赖
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
2
3
4
5
6
步骤2:使用PostMan发送JSON数据
步骤3:开启SpringMVC注解支持,在SpringMVC的配置类中开启SpringMVC的注解支持,这里面就包含了将JSON转换成对象的功能。
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
11
步骤4:在控制器方法的形参前面添加@RequestBody
注解
// 使用@RequestBody注解将外部传递的json数组映射到形参的集合对象中作为数据
@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
5
6
7
步骤5:启动程序,查看效果
# JSON对象数据
{
"name": "张三",
"age": 18
}
2
3
4
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user) {
System.out.println("pojo(json) 参数传递 user ==> " + user);
return "{'module': 'pojo for json param'}";
}
2
3
4
5
6
访问接口测试效果:
说明
address
为null
的原因是前端没有发送对应的数据给后端,如果想address
也有数据,我们需要修改前端传递的数据内容:
{
"name": "张三",
"age": 18,
"address": {
"province": "北京",
"city": "beijing"
}
}
2
3
4
5
6
7
8
再次发送请求,即可看到address
的数据被接收
# JSON对象数组
请求参数如下:
[
{ "name": "张三", "age": 16 },
{ "name": "李四", "age": 17 }
]
2
3
4
后端代码:
@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'}";
}
2
3
4
5
6
测试结果:
小结
SpringMVC接收JSON数据的实现步骤为:
- 导入jackson包
- 使用PostMan发送JSON数据
- 开启SpringMVC注解驱动,在配置类上添加
@EnableWebMvc
注解 Controller
方法的参数前添加@RequestBody
注解
# 知识点6:@EnableWebMvc
名称 | @EnableWebMvc |
---|---|
类型 | 配置类注解 |
位置 | SpringMVC配置类定义上方 |
作用 | 开启SpringMVC多项辅助功能 |
# 知识点7:@RequestBody
名称 | @RequestBody |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次 |
# @RequestBody和@RequestParam的区别
- 区别:
@RequestParam
用于接收URL地址传参,表单传参,application/x-www-form-urlencoded
@RequestBody
用于接收JSON数据application/json
- 应用
- 后期开发中,发送JSON数据为主,
@RequestBody
应用广泛 - 如果发送非JSON数据,可以选用
@RequestParam
接收请求参数
- 后期开发中,发送JSON数据为主,
# 日期类型参数传递
上面我们已经处理过简单数据类型、POJO数据类型、数组和集合数据类型以及JSON数据类型,接下来我们还得处理一种开发中比较常见的一种数据类型,日期类型。
日期类型比较特殊,因为对于日期的格式有很多种书写格式,比如:
- 2023-01-01
- 2023/01/01
- 01/01/2088
- ...
针对这么多日期格式, SpringMVC该如何接收呢?
# 步骤1:编写方法接收日期数据
在UserController
类中添加方法,把参数设置为日期类型
@RequestMapping("/dateParam")
@ResponseBody
public String dataParam(Date date) {
System.out.println("参数传递 date ==> " + date);
return "{'module': 'date param'}";
}
2
3
4
5
6
# 步骤2:启动Tomcat服务器
查看控制台是否报错,如果有错误输出,先解决错误。
# 步骤3:使用PostMan发送请求
# 步骤4:查看控制台
通过打印结果,我们发现SpringMVC可以接收日期类型的数据,那么如果我们将日期参数的格式换为其他类型,还能正常处理吗?
# 步骤5:更换日期格式
为了更好的看到程序运行结果,在控制器方法中再添加一个日期类型的参数
@RequestMapping("/dateParam")
@ResponseBody
public String dataParam(Date date, Date date1) {
System.out.println("参数传递 date ==> " + date);
System.out.println("参数传递 date1 ==> " + date1);
return "{'module': 'date param'}";
}
2
3
4
5
6
7
使用PostMan发送请求,携带两个不同格式的日期参数
发送请求后,页面会报400错误,控制台会抛出一个异常
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String]
to type [java.util.Date] for value '2023-01-01'; nested exception is java.lang.IllegalArgumentException]
2
从错误输出可以看出,抛出异常的原因是在将2023-01-01
转换成日期类型的时候失败了,因为SpringMVC默认支持的字符串转日期的格式为yyyy/MM/dd
,而我们现在传递的不符合其默认格式,SpringMVC就无法进行格式转换,因此报错。
解决方案也比较简单,需要使用@DateTimeFormat
@RequestMapping("/dateParam")
@ResponseBody
public String dataParam(Date date, @DateTimeFormat(pattern = "yyyy-MM-dd") Date date1) {
System.out.println("参数传递 date ==> " + date);
System.out.println("参数传递 date1(yyyy-MM-dd) ==> " + date1);
return "{'module': 'date param'}";
}
2
3
4
5
6
7
重启服务器,再次发送请求,SpringMVC就可以正确的进行日期参数转换了
# 步骤6:携带时间的日期
接下来我们再来发送一个携带时间的日期参数,看SpringMVC如何处理?
修改UserController
控制器中的dateParam
方法,再添加一个参数
@RequestMapping("/dateParam")
@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': 'date param'}";
}
2
3
4
5
6
7
8
9
重启服务器后,使用PostMan再次发送请求,携带三个日期格式不同的参数http://localhost/dateParam?date=2023/01/01&date1=2023-01-01&date2=2023-01-01 13:00:59
# 知识点8:@DateTimeFormat
名称 | @DateTimeFormat |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法的形参前面 |
作用 | 设定日期时间数据格式 |
相关属性 | pattern : 指定日期时间格式字符串 |
# 内部实现原理
上面我们学习了很多种类型的参数接收方式,那么不知道大家有没有这样的疑问?
- 前端传递字符串,后端可以使用日期
Date
接收 - 前端传递JSON数据,后端使用对象接收
- 前端传递字符串,后端使用
Integer
接收
在实际的请求数据传递过程中存在很多类型的转换,那么这个类型转换是谁负责实现的?SpringMVC,准确的说是SpringMVC框架中定义了一个Converter
接口,针对这个接口,结合不同的数据类型转换需求,就派生出了很多实现类。
package org.springframework.core.convert.converter;
/**
* S: the source type
* T: the target type
*/
public interface Converter<S, T> {
@Nullable
//该方法就是将从页面上接收的数据(S)转换成我们想要的数据类型(T)返回
T convert(S source);
}
2
3
4
5
6
7
8
9
10
注意
Converter
所属的包为org.springframework.core.convert.converter
SpringMVC为Converter
接口提供了很多种实现类,从来实现不同数据类型之间的转换,前面的例子中我们其实一直在使用这样的类型转换,如:
- 请求参数年龄数据(
String
->Integer
) - JSON数据转对象(
JSON
->POJO
) - 日期格式转换(
String
->Date
)
注意
后续使用SpringMVC框架,它的配置类切记加上@EnableWebMvc
注解,不要省略
# 响应
SpringMVC接收请求后,针对请求做一些处理,这个处理可以是转发Service
,Service
层再调用Dao
层完成,不管如何,请求处理完成后,都需要将处理结果告知给用户。
比如:根据用户ID查询用户信息、查询用户列表、新增用户等等。
对于响应,主要就包含两部分内容:
- 响应页面
- 响应数据
- 文本数据
- JSON数据
因为异步调用时目前企业中常用的主流方式,因此我们需要更关注的是如何返回JSON数据,对于其他了解即可。
# 响应-环境准备
- 基于Maven项目固件创建一个Web模块
- 在pom.xml文件中引入Servlet和SpringMVC依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>springmvc-06-response</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- 创建对应的配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.itheima.controller")
// 开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// Post请求中文乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 编写模型类
User
package com.itheima.domain;
public class User {
private String name;
private int age;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- webapp目录下创建page.jsp页面
<html>
<body>
<h2>Hello SpringMVC!</h2>
</body>
</html>
2
3
4
5
- 编写
UserController
package com.itheima.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}
2
3
4
5
6
7
8
最终创建好的项目目录结构如下:
# 响应页面(了解)
- 步骤一:在
UserController
控制器方法中设置返回页面
@RequestMapping("/toJumpPage")
// 注意:
// 1. 此处不能添加@ResponseBody,如果加了该注解
// 会直接将page.jsp作为字符串返回给前端
// 2. 方法需要返回String类型
public String toJumpPage() {
System.out.println("跳转页面");
return "page.jsp";
}
2
3
4
5
6
7
8
9
- 步骤二:启动服务器进行测试,此处涉及到页面跳转,因此不适合用PostMan进行测试,直接在打开浏览器,输入访问地址进行测试
# 响应文本数据(了解)
- 步骤一:设置返回文本内容
@RequestMapping("/toText")
// 注意此处注解就不能省略,如果省略,会把response text
// 当作页面名称去查找,如果没有对应的页面会返回404错误
@ResponseBody
public String toText() {
System.out.println("返回纯文本数据");
return "response text";
}
2
3
4
5
6
7
8
- 步骤二:启动服务器测试
# 响应JSON数据
- 响应POJO对象
@RequestMapping("/toJsonPojo")
@ResponseBody
public User toJsonPojo() {
System.out.println("返回json对象数据");
User user = new User();
user.setName("张三");
user.setAge(15);
return user;
}
2
3
4
5
6
7
8
9
返回值为实体类对象,在控制器方法中设置返回值为实体类类型,即可实现返回对应对象的JSON数据,需要依赖 @ResponseBody 注解和 @EnableWebMvc 注解
重新启动服务器,访问http://localhost/toJsonPojo
- 响应POJO集合对象
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList() {
System.out.println("返回json集合数据");
User user1 = new User();
user1.setName("张三");
user1.setAge(12);
User user2 = new User();
user2.setName("李四");
user2.setAge(13);
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
return users;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
重新启动服务器,访问http://localhost/toJsonList
# 知识点9:@ResponseBody
名称 | @ResponseBody |
---|---|
类型 | 方法/类注解 |
位置 | SpringMVC控制器方法定义上方或控制类上 |
作用 | 设置当前控制器返回值作为响应体,写在类上,该类的所有方法都有该注解功能 |
说明
- 该注解可以写在类上或方法上
- 写在类上就是该类下的所有方法都有
@ResponseBody
功能 - 当方法上有
@ResponseBody
注解后- 方法的返回值为字符串,会将其作为文本内容直接响应给前端
- 方法的返回值为对象,会将对象转换成JSON响应给前端
此处同样使用了类型转换,内部还是通过Converter
接口的实现类完成的,所以Converter
除了前面所说的功能外,还可以实现:
- 对象转JSON数据
- 集合转JSON数据
# REST风格
# REST简介
REST(Representational State Transfer),变现形式状态转换,它是一种软件架构的风格。当我们想表示一个网络资源的时候,可以使用两种方式:
- 传统风格资源描述
http://localhost/user/getById?id=1
查询id
为1
的用户信息http://localhost/user/saveUser
保存用户信息
- REST风格描述形式
http://localhost/user/1
http://localhost/user
传统方式一般是一个请求URL对应一种操作,这样做不仅麻烦,也不安全,因为懂编程的人读取到请求URL后,大概可以猜出当前URL实现的是一个什么样的操作。
查看REST风格的描述,会发现请求地址变得简单了,并且通过请求URL不容易猜出该URL的具体功能。
因此,REST的优点如下:
- 隐藏资源的访问行为,无法通过URL获悉对资源的具体操作
- 书写相对简化
但是问题也随之而来,比如一个相同的URL地址即可以是新增也可以是修改或者查询,那么我们该怎么区分到底是什么操作呢?
- 按照REST风格访问资源时使用行为动作来区分对资源进行的操作
http://localhost/users
查询全部用户信息 GET(查询)http://localhost/users/1
查询指定用户信息 GET(查询)http://localhost/users
添加用户信息 POST(新增/保存)http://localhost/users
修改用户信息 PUT(修改/更新)http://localhost/users/1
删除指定用户信息 DELETE(删除)
请求的方式有很多,但是我们比较常用的就是下面4种:GET、POST、PUT、DELETE。
按照不同的请求方式代表不同的操作类型:
- 发送GET请求用来查询
- 发送POST请求用来新增
- 发送PUT请求用来修改
- 发送DELETE请求用来删除
注意
上述行为是约定方式,约定不是规范,可以不遵循,所以称为REST风格,而不是REST规范
- REST提供了对应的架构方式,按照这种架构设计项目可以降低开发的复杂性,提高系统的可伸缩性
- REST中规定GET/POST/PUT/DELETE针对的是查询/新增/修改/删除,但是我们如果一定要用GET请求做删除,在程序中运行也是可以实现的
- 但是如果绝大多数团队都遵循这种风格,个别人不遵循,那么这些人写的代码就不会被推广。
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users
、books
、accounts
# RESTful入门案例
了解了什么是REST风格,后面我们还会经常遇到一个词RESTful,那这个有指代什么呢?根据REST风格对资源访问称为RESTful。
后面我们在进行开发的时候,大多都是遵循REST风格来访问我们后台的接口,所以可以说咱们后面都是基于RESTful来进行开发的。
# RESTful入门-环境准备
- 基于Maven项目骨架创建一个Web项目
- 在pom.xml文件中添加SpringMVC、servlet、jackson依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>springmvc-07-rest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- 创建配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.itheima.controller")
// 开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// POST请求中文乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- 编写模型类
User
和Book
package com.itheima.domain;
public class User {
private String name;
private int age;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.itheima.domain;
public class Book {
private String name;
private Double price;
public Book() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- 编写
UserController
和BookController
package com.itheima.controller;
import com.itheima.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping("/user/save")
@ResponseBody
public String save(@RequestBody User user) {
System.out.println("user save ... " + user);
return "{'module': 'user save'}";
}
@RequestMapping("/user/delete")
@ResponseBody
public String delete(Integer id) {
System.out.println("user delete ... " + id);
return "{'module': 'user delete'}";
}
@RequestMapping("/user/update")
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("user update ...");
return "{'module': 'user update'}";
}
@RequestMapping("/user/getById")
@ResponseBody
public String getById(Integer id) {
System.out.println("user getById ... " + id);
return "{'module': 'user getById'}";
}
@RequestMapping("/user/getAll")
@ResponseBody
public String getAll() {
System.out.println("user getAll ...");
return "{'module': 'user getAll'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.itheima.controller;
import com.itheima.domain.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class BookController {
@RequestMapping("/book/save")
@ResponseBody
public String save(@RequestBody Book book) {
System.out.println("book save ... " + book);
return "{'module': 'book save'}";
}
@RequestMapping("/book/delete")
@ResponseBody
public String delete(Integer id) {
System.out.println("book delete ... " + id);
return "{'module': 'book delete'}";
}
@RequestMapping("/book/update")
@ResponseBody
public String update(@RequestBody Book book) {
System.out.println("book update ... " + book);
return "{'module': 'book update'}";
}
@RequestMapping("/book/getById")
@ResponseBody
public String getById(Integer id) {
System.out.println("book getById ... " + id);
return "{'module': 'book getById'}";
}
@RequestMapping("/book/getAll")
@ResponseBody
public String getAll() {
System.out.println("book getAll ...");
return "{'module': 'book getAll'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
最后创建好的项目目录结构如下
# RESTful入门-思路分析
需求:将上面
UserController
和BookController
中的方法都替换成RESTful开发方式
- 之前不同的请求有不同的路径,现在需要将其修改为统一的请求路径 修改前:新增:
/user/save
,/book/save
,... 修改后:增删改查/users
,/books
- 根据GET查询、POST新增、PUT修改、DELETE删除对方法的请求方式进行限定
- 需要考虑发送请求的过程中如何设置请求参数?
# RESTful入门-修改控制器方法为RESTful风格
- 新增
@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user) {
System.out.println("user save ... " + user);
return "{'module': 'user save'}";
}
2
3
4
5
6
说明
- 将请求路径修改为
/users
- 访问该方法使用POST:
http://localhost/users
- 访问该方法使用POST:
- 使用
method
属性限定该方法的请求方式为POST- 如果发送的不是POST请求,比如发送GET请求,则会报错
- 删除
// 设置当前请求方式为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/users", method = RequestMethod.DELETE)
@ResponseBody
public String delete(Integer id) {
System.out.println("user delete ... " + id);
return "{'module': 'user delete'}";
}
2
3
4
5
6
7
- 将请求路径更改为
/users
- 范文该方法使用DELETE发起请求:
http://localhost/users
- 范文该方法使用DELETE发起请求:
重启服务器,使用PostMan发起请求成功,但是删除方法没有获取到要删除数据的id
,那么基于RESTful开发,如何携带请求参数呢?
传递路径参数:前端发送请求的时候使用http://localhost/users/1
,路径中的1
就是我们想要传递的参数。
后端获取参数,需要做如下修改:
- 修改
@RequestMapping
的value
属性,将其修改为/users/{id}
,目的是和路径匹配 - 在方法的形参前添加
@PathVariable
注解
// 设置当前请求方式为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete ... " + id);
return "{'module': 'user delete'}";
}
2
3
4
5
6
7
两个问题
- 如果方法形参的名称和路径
{}
中的值不一致,该如何?
- 如果有多个参数需要传递该如何设置路径?前端发送请求的时候可以使用
http://localhost/users/1/zhangsan
,路径中的1
和zhangsan
就是我们想要传递的两个参数,那么后端代码就要做如下修改:
@Controller
public class UserController {
//设置当前请求方法为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/users/{id}/{name}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id,@PathVariable String name) {
System.out.println("user delete..." + id+","+name);
return "{'module':'user delete'}";
}
}
2
3
4
5
6
7
8
9
10
- 修改
// 设置当前请求方法为PUT,表示REST风格中的修改操作
@RequestMapping(value = "/users", method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("user update ...");
return "{'module': 'user update'}";
}
2
3
4
5
6
7
- 根据ID查询
// 设置当前请求方法为GET,表示REST风格中的查询操作
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id) {
System.out.println("user getById ... " + id);
return "{'module': 'user getById'}";
}
2
3
4
5
6
7
- 查询所有
@RequestMapping(value = "/users", method = RequestMethod.GET)
@ResponseBody
public String getAll() {
System.out.println("user getAll ...");
return "{'module': 'user getAll'}";
}
2
3
4
5
6
小结
RESTful入门案例,我们小掌握的内容如下:
- 设定HTTP请求动作:
@RequestMapping(value="", method=RequestMethod.POST|GET|PUT|DELETE)
- 设定请求参数(路径变量)
@RequestMapping(value="/users/{id}", method=RequestMethod.DELETE)
public String delete(@PathVariable Integer id)
# 知识点10:@PathVariable
名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
@RequestBody, @RequestParam, @PathVariable区别和应用
- 区别
@RequestParam
用于接收URL地址或表单传参@RequestBody
用于接收JSON数据@PathVariable
用于接收路径传参,使用{参数名称}
描述路径参数
- 应用
- 后期开发中,发送请求参数超过1个时,建议以JSON格式传参为主,
@RequestBody
应用较广 - 如果发送非JSON格式数据,选用
@RequestParam
接收请求参数 - 采用RESTful进行开发,当参数较少时,可以采用
@PathVariable
接收请求路径变量。
- 后期开发中,发送请求参数超过1个时,建议以JSON格式传参为主,
# RESTful快速开发
上面我们基于RESTful开发,大家觉不觉得还是有些麻烦,比如下图用红线框起来的部分:
问题一:每个方法的@RequestMapping
注解中都定义了访问路径/users
,重复性有点高
问题二:每个方法的@RequestMapping
注解中都要使用method
属性定义请求方式,还是重复代码
问题三:每个方法响应JSON都需要加上@ResponseBody
注解,依旧是重复代码
那么,这三个问题还如何解决呢?
package com.itheima.controller;
import com.itheima.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
//@Controller
@RestController // @Controller + @ResponseBody
@RequestMapping("/users") // 抽取
public class UserController {
// @RequestMapping(value = "/users", method = RequestMethod.POST)
@PostMapping // 简化整合
// @ResponseBody
public String save(@RequestBody User user) {
System.out.println("user save ... " + user);
return "{'module': 'user save'}";
}
// 设置当前请求方式为DELETE,表示REST风格中的删除操作
// @RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/{id}") // 简化整合
// @ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete ... " + id);
return "{'module': 'user delete'}";
}
// 设置当前请求方法为PUT,表示REST风格中的修改操作
// @RequestMapping(value = "/users", method = RequestMethod.PUT)
@PutMapping
// @ResponseBody
public String update(@RequestBody User user) {
System.out.println("user update ...");
return "{'module': 'user update'}";
}
// 设置当前请求方法为GET,表示REST风格中的查询操作
// @RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@GetMapping("/{id}")
// @ResponseBody
public String getById(@PathVariable Integer id) {
System.out.println("user getById ... " + id);
return "{'module': 'user getById'}";
}
// @RequestMapping(value = "/users", method = RequestMethod.GET)
@GetMapping
// @ResponseBody
public String getAll() {
System.out.println("user getAll ...");
return "{'module': 'user getAll'}";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
对于刚才的问题,我们都有对应的解决方案:
问题一解决方案:每个方法的@RequestMapping
注解中都定义了访问路径/users
将
@RequestMapping
提取到类上面,用来定义所有方法共同的访问路径。
问题二解决方案:每个方法的@RequestMapping
注解中都要使用method
属性定义请求方式
使用
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
代替
问题三解决方案:每个方法响应JSON都需要加上@ResponseBody
注解
- 将
@ResponseBody
提取到类上面,让所有的方法都有@ResponseBody
功能- 使用
@RestController
注解替换@Controller
与@ResponseBody
注解,简化书写
# 知识点11:@RestController
名称 | @RestController |
---|---|
类型 | 类注解 |
位置 | 基于SpringMVC的RESTful开发控制器类定义上方 |
作用 | 设置当前控制器类为RESTful风格,等同于@Controller 与@ResponseBody 两个注解的组合功能 |
# 知识点12:@GetMapping @PostMapping @PutMapping @DeleteMapping
名称 | @GetMapping @PostMapping @PutMapping @DeleteMapping |
---|---|
类型 | 方法注解 |
位置 | 基于SpringMVC的RESTful控制器方法定义上方 |
作用 | 设置当前控制器方法请求访问路径与请求方式,每种对应一个请求动作,例如@GetMapping 对应GET请求 |
相关属性 | value (默认):请求访问路径 |
# RESTful案例
# RESTful案例-需求分析
需求一:图书列表查询,后台返回图书数据,将数据展示到页面上
需求二:新增图书,将新增图书的数据传递到后台,并在控制台打印
说明
此案例的重点是在SpringMVC中如何使用RESTful实现前后台交互,因此本案例不实现和数据库进行交互的功能,所有功能都使用模拟数据来完成开发。
步骤分析:
- 搭建项目,引入依赖坐标
- 编写SpringMVC相关配置
- 编写POJO类
- 编写Controller类,提供两个控制器方法,一个用来列表查询,一个用来新增
- 在控制器上使用RESTful进行路径配置
- 完成请求参数的接收和请求结果的响应
- 使用PostMan进行测试
- 将资料中的前端页面拷贝到项目的webapp目录下
- 页面发送Ajax请求
- 完成页面数据的展示
# RESTful案例-环境准备
- 创建一个Web项目
- 在pom.xml文件中引入SpringMVC、Servlet、jackson依赖坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>springmvc-08-rest-case</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- 创建配置类
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.itheima.controller")
// 开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
// POST请求中文乱码处理
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- 编写模型类
Book
package com.itheima.domain;
public class Book {
private Integer id;
private String type;
private String name;
private String description;
public Book() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", type='" + type + '\'' +
", name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
- 编写
BookController
package com.itheima.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/books")
public class UserController {
}
2
3
4
5
6
7
8
9
10
最终创建好的项目结构如下:
# RESTful案例-后台接口开发
- 编写
BookController
,使用RESTful进行配置
package com.itheima.controller;
import com.itheima.domain.Book;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/books")
public class UserController {
@PostMapping
public String save(@RequestBody Book book) {
System.out.println("book save ==> " + book);
return "{'module': 'book save success'}";
}
@GetMapping
public List<Book> getAll() {
System.out.println("book getAll is running ...");
List<Book> books = new ArrayList<>();
Book book1 = new Book();
book1.setType("计算机");
book1.setName("SpringMVC入门教程");
book1.setDescription("小试牛刀");
books.add(book1);
Book book2 = new Book();
book2.setType("计算机");
book2.setName("SpringMVC实战教程");
book2.setDescription("一代宗师");
books.add(book2);
Book book3 = new Book();
book3.setType("计算机丛书");
book3.setName("SpringMVC实战教程进阶");
book3.setDescription("一代宗师呕心创作");
books.add(book3);
return books;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
使用PostMan进行测试
- 测试新增
// 请求参数 { "type":"计算机丛书", "name":"SpringMVC终极开发", "description":"这是一本好书" }
1
2
3
4
5
6- 测试查询所有
# RESTful案例-前端页面实现
- 拷贝静态页面:将课程资料中的功能页面内容拷贝到项目的webapp目录下
- 启动服务器,访问pages目录下的books.html页面,
http://localhost/pages/books.html
出现错误的原因
- 因为SpringMVC拦截了静态资源,根据*/pages/books.html会去Controller控制器中寻找对应的方法,肯定找不到所以会报404*错误。
- SpringMVC为什么会拦截我们的静态资源?原因在配置类
ServletContainersInitConfig
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; // 配置的是/,因此所有的请求,SpringMVC都会进行拦截
}
2
3
4
- 如何解决?配置SpringMVC将静态资源放行,不再拦截
package com.itheima.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
// 设置静态资源访问过滤,当前类需要设置为配置类
// 并且要被扫描加载
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 当访问/pages/???时,SpringMVC不再拦截,由Tomcat响应/pages目录下的页面
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
该配置类在config包下,SpringMVC扫描的是controller包,因此该配置还要想生效,还需要在SpringMvcConfig
配置类中添加扫描路径
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan({"com.itheima.controller", "com.itheima.config"})
// 开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
11
12
或者
package com.itheima.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan("com.itheima")
// 开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
2
3
4
5
6
7
8
9
10
11
12
- 修改books.html页面,使用axios发送异步请求
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<title>SpringMVC案例</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/style.css">
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>图书管理</h1>
</div>
<div class="app-container">
<div class="box">
<div class="filter-container">
<el-input placeholder="图书名称" style="width: 200px;" class="filter-item"></el-input>
<el-button class="dalfBut">查询</el-button>
<el-button type="primary" class="butT" @click="openSave()">新建</el-button>
</div>
<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
<el-table-column type="index" align="center" label="序号"></el-table-column>
<el-table-column prop="type" label="图书类别" align="center"></el-table-column>
<el-table-column prop="name" label="图书名称" align="center"></el-table-column>
<el-table-column prop="description" label="描述" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini">编辑</el-button>
<el-button size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
<!-- 新增标签弹层 -->
<div class="add-form">
<el-dialog title="新增图书" :visible.sync="dialogFormVisible">
<el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="saveBook()">确定</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</body>
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data:{
dataList: [],//当前页要展示的分页列表数据
formData: {},//表单数据
dialogFormVisible: false,//增加表单是否可见
dialogFormVisible4Edit:false,//编辑表单是否可见
pagination: {},//分页模型数据,暂时弃用
},
//钩子函数,VUE对象初始化完成后自动执行
created() {
this.getAll();
},
methods: {
// 重置表单
resetForm() {
//清空输入框
this.formData = {};
},
// 弹出添加窗口
openSave() {
this.dialogFormVisible = true;
this.resetForm();
},
//添加
saveBook () {
axios.post("/books",this.formData).then((res)=>{
});
},
//主页列表查询
getAll() {
axios.get("/books").then((res)=>{
this.dataList = res.data;
});
},
}
})
</script>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
- SpringMVC简介
- SpringMVC概述
- SpringMVC入门案例
- 案例需求分析
- 知识点1:@Controller
- 知识点2:@RequestMapping
- 知识点3:@ResponseBody
- 入门案例总结
- 入门案例工作流程分析
- bean加载控制
- 知识点4:@ComponentScan
- PostMan工具的使用
- PostMan简介
- PostMan安装
- PostMan使用
- 请求与响应
- 设置请求映射路径
- 请求参数
- 五种类型参数传递
- 知识点5:@RequestParam
- JSON数据传输参数
- 知识点6:@EnableWebMvc
- 知识点7:@RequestBody
- 日期类型参数传递
- 知识点8:@DateTimeFormat
- 响应
- 知识点9:@ResponseBody
- REST风格
- REST简介
- RESTful入门案例
- 知识点10:@PathVariable
- RESTful快速开发
- 知识点11:@RestController
- 知识点12:@GetMapping @PostMapping @PutMapping @DeleteMapping
- RESTful案例