目录
“为什么要学 SpringMVC?它和 Servlet 是什么关系?”
“什么是异步?为什么异步交互中常用 JSON 格式?异步请求和 JSON 如何配合?”
一、概述
SpringMVC主要负责
1 SpringMVC的常用组件
2 SpringMVC的工作流程解析
3 Spring 和 SpringMVC Bean 管理问题总结
问题:如何避免 Spring 加载到 SpringMVC 的 Bean?
解决方案一:分开扫描 Spring 和 SpringMVC 的 Bean
解决方案二:通过 excludeFilters 排除 Controller Bean
@ComponentScan
二、请求与响应
2.1 普通参数
2.2 POJO参数
2.3 嵌套POJO类型参数
2.4 数组类型参数
2.5 集合类型参数
2.6 JSON数据传输参数
(1)JSON 普通数组
(2)JSON对象
(3)JSON对象数组
接收 JSON 数据的步骤
2.7 日期类型参数传递
三 内部实现原理
类型转换的核心接口:
SpringMVC 中的类型转换工作
四 响应
4.1 响应的两种主要方式
4.2 响应页面
4.3 响应文本数据
方法返回纯文本
4.4 响应JSON数据
(1)返回POJO对象
(2)返回POJO 集合
4.5 核心注解@ResponseBody
作用
使用位置
五 REST风格
5.1 REST简介
5.2 REST的优点
5.3 常用的HTTP请求方法及其语义
5.4 REST风格的约定
5.5 什么是RESTful
5.6 RESTful开发的常见规范
5.7 RESTful示例总结
5.8 RESTful开发的注意事项
5.9 总结
5.10 RESTful入门
5.11 RESTful快速开发
“为什么要学 SpringMVC?它和 Servlet 是什么关系?”
参考答案 嘻嘻:
Servlet 是 Java Web 开发的基础技术,用于处理 HTTP 请求和响应,但它开发起来比较繁琐,需要手动处理参数、构造响应,代码耦合度高,不适合复杂项目。
SpringMVC 是基于 Servlet 的高级框架,它通过前端控制器 DispatcherServlet 接管所有 HTTP 请求,然后自动把请求转发到相应的 Controller。你只需要专注写业务逻辑,而不用去操心怎么解析请求、怎么返回响应。SpringMVC 会帮你自动完成这些工作,比如自动绑定请求参数,处理视图渲染等。
跟 Servlet 比起来,SpringMVC 的最大优点是,它采用了 MVC 模式(即模型-视图-控制器),把控制逻辑、业务逻辑和视图展示都分开了,代码更加清晰、易于维护。而且它提供了很多有用的功能,比如数据校验、拦截器、RESTful API 支持等等,这些都能大大提高开发效率,尤其是在做比较复杂的 Web 项目时,SpringMVC 更加得心应手。
比如,如果你用 Servlet 写一个接口,你得手动去解析请求参数、处理返回的内容。而用 SpringMVC,你只需要在 Controller 类里用注解标记一下,剩下的 SpringMVC 会自动帮你做,开发起来就省事多了。
所以,学 SpringMVC 主要是因为它不仅简化了开发流程,还能跟 Spring 生态里的其他技术(比如 Spring Boot)很好地集成,帮助我们快速开发高效、可维护的 Web 应用。
“什么是异步?为什么异步交互中常用 JSON 格式?异步请求和 JSON 如何配合?”
异步是一种非阻塞的交互方式,前端发送请求后不用等待服务器返回结果,可以继续执行其他逻辑,等服务器返回数据时再更新页面部分内容。这样能避免页面卡顿或整体刷新,显著提升用户体验。
异步交互中常用 JSON 格式是因为它轻量、结构清晰,适合网络传输,且易于解析,前端 JavaScript 可以直接处理。JSON 也已成为 RESTful API 和现代前后端交互的标准格式。
在异步交互中,前端通过 AJAX 或 Fetch 等技术发送请求,服务器返回 JSON 格式数据,前端解析后动态更新页面。异步请求与 JSON 的结合使前后端数据传输更加高效,页面交互更加流畅。
一、概述
SpringMVC主要负责
controller如何接收请求和数据
如何将请求和数据转发给业务层
如何将响应数据转换成json发回到前端
1 SpringMVC的常用组件
(1)DispatcherServlet
是一种前端控制器,由框架提供。
作用:统一处理请求和响应。除此之外还是整个流程控制的中心,由 DispatcherServlet 来调用其他组件,处理用户的请求
(2) HandlerMapping
处理器映射器,由框架提供。
作用:根据请求的 url、method 等信息来查找具体的 Handler(一般来讲是Controller)
(3) Handler(一般来讲是Controller)
处理器,注意,这个需由工程师自己开发。
作用:在 DispatcherServlet 的控制下,Handler对具体的用户请求进行处理
(4)HandlerAdapter
处理器适配器 ,由框架提供。
作用:根据映射器找到的处理器 Handler 信息,按照特定的规则去执行相关的处理器 Handler。
(5)小结
Handler 是用来干活的工具;
HandlerMapping 用于根据需要干的活找到相应的工具;
HandlerAdapter 是使用工具干活的人
(6) ViewResolver
视图解析器,由框架提供。
作用: ViewResolver 负责将处理结果生成 View 视图。 ViewResolver 首先根据逻辑视图名解析成物理图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
(7)view
视图,工程师自己开发
作用:View接口的职责就是接收model对象、Request对象、Response对象,并渲染输出结果给Response对象。
2 SpringMVC的工作流程解析
1. 用户通过浏览器发起 HttpRequest 请求到前端控制器 (DispatcherServlet)。
2. DispatcherServlet 将用户请求发送给处理器映射器 (HandlerMapping)。
3. 处理器映射器 (HandlerMapping)会根据请求,找到负责处理该请求的处理器,并将其封装为处理器执行链 返回 (HandlerExecutionChain) 给 DispatcherServlet
4. DispatcherServlet 会根据 处理器执行链 中的处理器,找到能够执行该处理器的处理器适配器(HandlerAdaptor) --注,处理器适配器有多个
5. 处理器适配器 (HandlerAdaptoer) 会调用对应的具体的 Controller
6. Controller 将处理结果及要跳转的视图封装到一个对象 ModelAndView 中并将其返回给处理器适配器 (HandlerAdaptor)
7. HandlerAdaptor 直接将 ModelAndView 交给 DispatcherServlet ,至此,业务处理完毕
8. 业务处理完毕后,我们需要将处理结果展示给用户。于是DisptcherServlet 调用 ViewResolver,将 ModelAndView 中的视图名称封装为视图对象
9. ViewResolver 将封装好的视图 (View) 对象返回给 DIspatcherServlet
10. DispatcherServlet 调用视图对象,让其自己 (View) 进行渲染(将模型数据填充至视图中),形成响应对象 (HttpResponse)
11. 前端控制器 (DispatcherServlet) 响应 (HttpResponse) 给浏览器,展示在页面上。
3 Spring 和 SpringMVC Bean 管理问题总结
在 Spring 和 SpringMVC 中,二者分别负责不同层次的 Bean 管理:
-
Spring 负责的 Bean(业务逻辑相关):
- Service 层: 负责业务逻辑的类,比如
UserService
、OrderService
等。 - DAO 层: 数据访问相关的类,比如
UserDao
。 - 功能性 Bean: 如
DataSource
、SqlSessionFactoryBean
、MapperScannerConfigurer
等。
- Service 层: 负责业务逻辑的类,比如
-
SpringMVC 负责的 Bean(表现层相关):
- Controller 层: 负责处理用户请求的类,比如
UserController
、OrderController
。
- Controller 层: 负责处理用户请求的类,比如
问题:如何避免 Spring 加载到 SpringMVC 的 Bean?
因为 Spring 和 SpringMVC 的功能范围不同,二者需要各自管理自己的 Bean。若不进行分离(如 @ComponentScan
配置不明确),Spring 可能会误扫描到 Controller 层的类,导致功能混乱或错误。
解决方案一:分开扫描 Spring 和 SpringMVC 的 Bean
在 Spring 的配置类中精确设置扫描范围,只扫描需要的包(如 service
和 dao
包),避免扫描 controller
包。
解决方案二:通过 excludeFilters
排除 Controller
Bean
如果包结构中 controller
包和 service
包处于同级目录,可以通过 excludeFilters
属性排除掉 Spring 扫描时的 controller
Bean:
@Configuration
@ComponentScan(
basePackages = "com.itheima",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
// Spring 配置,排除 controller Bean
}
excludeFilters属性:设置扫描加载bean时,排除的过滤规则
type属性:设置排除规则
type = FilterType.ANNOTATION
是按照注解类型排除。classes = Controller.class
指定排除的注解为@Controller
。
@ComponentScan
- 作用: 配置扫描路径,加载指定包下的 Bean。
- 常用属性:
basePackages
:指定扫描的包路径。excludeFilters
:排除不需要加载的 Bean。- 常用类型:
ANNOTATION
:按注解排除(如排除@Controller
)。ASSIGNABLE_TYPE
:按具体类型排除。
- 常用类型:
includeFilters
:仅加载指定的 Bean(类似excludeFilters
的反向操作)。
二、请求与响应
2.1 普通参数
普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。
如果形参与地址参数名不一致该如何解决?
使用@RequestParam注解
2.2 POJO参数
简单数据类型一般处理的是参数个数比较少的请求,如果参数比较多,那么后台接收参数的时候就比较复杂,这个时候我们可以考虑使用POJO数据类型。
POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数
注意:请求参数key的名称要和POJO中属性的名称一致,否则无法封装。
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+user);
return "{'module':'pojo param'}";
}
public class User {
private String name;
private int age;
//setter...getter...略
}
2.3 嵌套POJO类型参数
如果POJO对象中嵌套了其他的POJO类,如
public class Address {
private String province;
private String city;
//setter...getter...略
}
public class User {
private String name;
private int age;
private Address address;
//setter...getter...略
}
嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+user);
return "{'module':'pojo param'}";
}
2.4 数组类型参数
举个简单的例子,如果前端需要获取用户的爱好,爱好绝大多数情况下都是多个,如何发送请求数据和接收数据呢?
前端可以通过 GET
或 POST
请求发送多个相同名称的参数(如 hobbies=足球&hobbies=篮球&hobbies=音乐
)或者使用 JSON 格式发送数组。后端可以通过 @RequestParam
注解接收这些参数。Spring 自动将多个相同名称的请求参数绑定到数组或集合中。
数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数
//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
return "{'module':'array param'}";
}
2.5 集合类型参数
//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "{'module':'list param'}";
}
集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,用@RequestParam绑定参数关系
-
@RequestParam
注解:- 用于绑定请求参数和方法形参的关系。
- 常用属性:
required
:是否为必传参数,默认值为true
。defaultValue
:设置参数的默认值,用于处理参数缺失的情况。
2.6 JSON数据传输参数
(1)JSON 普通数组
前端发送: ["value1", "value2", "value3"]
后端接收:
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes) {
System.out.println("JSON 数组参数 ==> " + likes);
return "{'module':'list json array param'}";
}
(2)JSON对象
前端发送:
{
"name": "itcast",
"age": 15,
"address": {
"province": "beijing",
"city": "beijing"
}
}
后端接收:
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user) {
System.out.println("JSON 对象参数 ==> " + user);
return "{'module':'pojo json param'}";
}
(3)JSON对象数组
前端发送:
[
{"name": "itcast", "age": 15},
{"name": "itheima", "age": 12}
]
后端接收:
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list) {
System.out.println("JSON 对象数组参数 ==> " + list);
return "{'module':'list pojo for json param'}";
}
接收 JSON 数据的步骤
(1)添加依赖
在 pom.xml
中引入 Jackson 库(Spring 默认使用 Jackson 处理 JSON 转换)
(2)前端发送数据
(3)开启注解支持
在 SpringMVC 配置类中添加 @EnableWebMvc
:
@Configuration
@EnableWebMvc
public class SpringMvcConfig {
}
(4)使用@RequestBody注解
在 Controller 方法形参前添加 @RequestBody
,将 JSON 数据映射到对象或集合中。
2.7 日期类型参数传递
SpringMVC 默认支持的日期格式为 yyyy/MM/dd
。
如果传递其他格式的日期(如 yyyy-MM-dd
),SpringMVC 会报 MethodArgumentTypeMismatchException
错误。
如果需要支持多种格式的日期类型参数,可以使用 @DateTimeFormat
注解。
单个日期
@RequestMapping("/dataParam")
@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':'data param'}";
}
支持带时间的日期格式
@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'}";
}
GET 请求示例:
http://localhost/dataParam?date=2088/08/08&date1=2088-08-08&date2=2088/08/08 08:08:08
三 内部实现原理
类型转换的核心接口:
-
Converter
接口- 作用: 用于实现不同数据类型之间的转换。
- 示例:
- 字符串 → 整数:
String → Integer
- 字符串 → 日期:
String → Date
- 字符串 → 整数:
-
HttpMessageConverter
接口- 作用: 用于实现对象与 JSON 数据之间的转换。
- 示例:
- 将 JSON 数据转为对象。
- 将对象转为 JSON 数据。
SpringMVC 中的类型转换工作
SpringMVC 提供了 Converter
接口的多种实现类,帮助完成常见数据类型之间的转换(如日期、数字等)。
SpringMVC 的注解驱动功能(@EnableWebMvc
)会自动启用这些类型转换功能。
总结:
SpringMVC 的类型转换由两种核心接口负责:
- Converter 接口: 实现简单的数据类型转换(如字符串 → 日期,字符串 → 整数)。Spring 提供了许多默认实现,也支持自定义。
- HttpMessageConverter 接口: 实现对象与 JSON 数据的转换(如 JSON → 对象,对象 → JSON),Spring 提供了
MappingJackson2HttpMessageConverter
作为默认实现。
这些功能通过 @EnableWebMvc
自动启用。如果默认实现不满足需求,还可以通过自定义 Converter
或 HttpMessageConverter
实现扩展。
四 响应
SpringMVC 接收到请求和数据后,会进行处理,最终需要将处理结果返回给用户。返回结果可以是页面、纯文本数据、JSON 数据等。
4.1 响应的两种主要方式
- 响应页面:
- 跳转到指定的页面(如 JSP 页面)。
- 响应数据:
- 返回文本数据。
- 返回 JSON 数据(对象或集合)。
4.2 响应页面
在 Controller 方法中返回一个页面路径,SpringMVC 默认会将路径映射到 webapp
下的页面文件。
@Controller
public class UserController {
@RequestMapping("/toJumpPage")
public String toJumpPage() {
System.out.println("跳转页面");
return "page.jsp"; // 返回页面路径
}
}
不要添加 @ResponseBody
注解,否则页面路径会被作为纯文本返回。
4.3 响应文本数据
方法返回纯文本
使用 @ResponseBody
注解将方法返回的值作为响应体,直接响应文本数据到前端。
@Controller
public class UserController {
@RequestMapping("/toText")
@ResponseBody
public String toText() {
System.out.println("返回纯文本数据");
return "response text"; // 返回纯文本
}
}
必须添加 @ResponseBody
注解,否则 SpringMVC 会尝试将返回值当作页面路径处理。
4.4 响应JSON数据
(1)返回POJO对象
返回值为实体类对象,SpringMVC 会自动将对象转换为 JSON 格式。
-
需要:
@ResponseBody
注解。@EnableWebMvc
注解,启用 JSON 转换功能。
@Controller
public class UserController {
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO() {
System.out.println("返回 JSON 对象数据");
User user = new User();
user.setName("itcast");
user.setAge(15);
return user; // 返回 JSON 对象
}
}
(2)返回POJO 集合
返回值为实体类集合,SpringMVC 会自动将集合转换为 JSON 格式。
@Controller
public class UserController {
@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<>();
userList.add(user1);
userList.add(user2);
return userList; // 返回 JSON 集合
}
}
结果:
[
{
"name": "传智播客",
"age": 15
},
{
"name": "黑马程序员",
"age": 12
}
]
4.5 核心注解@ResponseBody
作用
- 将方法返回值直接作为 HTTP 响应体返回,而不是页面路径。
- 返回值为字符串: 返回纯文本数据。
- 返回值为对象: 将对象或集合转换为 JSON 数据并返回。
使用位置
- 方法级别:
对单个方法启用@ResponseBody
。 - 类级别:
对整个控制器类启用@ResponseBody
,类中的所有方法都具有该功能。
类型转换通过 Converter
接口实现。
JSON 转换通过 MappingJackson2HttpMessageConverter
实现。
五 REST风格
5.1 REST简介
- REST 是一种软件架构风格,全称为 “表现形式状态转换”。
- 核心思想是:通过统一的资源标识(URL)和请求方法(HTTP 动词)来描述资源的操作。
- 它以一种简单、直观的方式定义了资源的操作,特别适合现代的分布式系统和 Web 服务开发。
5.2 REST的优点
-
隐藏资源的访问行为:
- 通过统一的 URL,无需暴露具体的操作行为。
- URL 不再描述操作细节,而是描述资源本身。
-
书写简化:
- 一个 URL 表示资源,减少了冗余的路径设计。
- 通过请求方法(GET、POST 等)区分操作。
-
更安全:
- 操作类型由 HTTP 动词决定,URL 变得不可预测,降低安全风险。
-
降低开发复杂性:
- 提供了一种规范的资源访问方式,降低了代码耦合性。
-
提高系统可扩展性:
- REST 风格的系统设计更清晰,资源层次结构更直观。
5.3 常用的HTTP请求方法及其语义
5.4 REST风格的约定
-
模块名称使用复数形式:
- 模块通常以复数形式命名,表示一类资源,而非单个资源。
- 示例:
- 用户模块:
/users
- 图书模块:
/books
- 用户模块:
-
请求方法区分操作:
- 不同的请求方法对应不同的操作类型。
- 示例:
GET
:获取资源POST
:新增资源PUT
:修改资源DELETE
:删除资源
-
路径参数传递:
- 通过路径中的占位符
{}
表示动态参数。 - 示例:
- 查询指定用户:
/users/{id}
- 删除指定用户:
/users/{id}
- 查询指定用户:
- 通过路径中的占位符
5.5 什么是RESTful
根据REST风格对资源进行访问称为RESTful
5.6 RESTful开发的常见规范
-
资源路径:
- 使用统一的路径表示一类资源,避免操作名称出现在路径中。
- 示例:
/users
:表示用户资源。/books
:表示图书资源。
-
路径参数:
- 动态参数通过路径占位符
{}
表示。 - 示例:
/users/{id}
:表示用户的唯一标识。
- 动态参数通过路径占位符
-
请求方法:
- 通过 HTTP 动词明确资源的操作类型。
- 示例:
GET /users
:查询所有用户。POST /users
:新增用户。PUT /users
:修改用户。DELETE /users/{id}
:删除指定用户。
-
统一返回 JSON 数据:
- 所有的响应统一返回 JSON 格式,便于前后端交互。
5.7 RESTful示例总结
5.8 RESTful开发的注意事项
-
规范命名:
- 模块名称使用复数形式,如
users
、books
。 - 避免路径中包含动词,操作通过请求方法指定。
- 模块名称使用复数形式,如
-
合理选择请求方法:
- 不要用
GET
做删除操作,这虽然可以实现,但不符合 REST 风格。
- 不要用
-
路径参数优先:
- 参数少时使用路径参数(
@PathVariable
),如 ID。 - 参数多时使用 JSON 数据(
@RequestBody
)。
- 参数少时使用路径参数(
-
统一数据格式:
- 请求体和响应体尽量使用 JSON 格式。
5.9 总结
REST 是一种资源访问的架构风格,使用 URL 表示资源,通过 HTTP 请求方法(GET、POST、PUT、DELETE)描述操作行为;RESTful 是基于 REST 风格的开发方式,通过路径、参数和方法的规范设计,使得系统更简洁、规范和易于维护。
5.10 RESTful入门
(1)设定Http请求动作(动词)
@RequestMapping(value="",method = RequestMethod.POST|GET|PUT|DELETE)
(2)设定请求参数(路径变量)
@RequestMapping(value="/users/{id}",method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){
}