Appearance
SpringMVC
1.1 SpringMVC 简介
Spring MVC 全称:Spring Web MVC,是 Spring 框架的一部分,专注于实现 Web 应用程序的模型-视图-控制器(Model-View-Controller, MVC)设计模式。它为构建灵活且松耦合的 Web 应用提供了强大的功能,同时保持了与 Spring 框架其他模块的良好集成。
1.2 SpringMVC 特点
- Spring 家族原生产品:与 IOC 容器等基础设施无缝对接
- 全方位覆盖:表述层各细分领域需要解决的问题提供全面解决方案
- 开发效率高:代码清新简洁,大幅度提升开发效率
- 组件化程度高:内部组件化程度高,可插拔式组件即插即用
- 性能卓越:性能卓著,适合现代大型、超大型互联网项目要求
- 异常处理机制:
- 使用
@ExceptionHandler注解定义全局异常处理器 - 支持自定义错误页面和 HTTP 状态码返回
- 使用
- RESTful 支持:
- 内置对 RESTful Web 服务的支持
- 支持多种数据格式(JSON、XML)
- AOP 集成:
- 与 Spring AOP 集成良好
- 通过拦截器添加横切关注点
1.3 核心组件及调用流程
1.3.1 SpringMVC 核心组件
DispatcherServlet
- 功能:前端控制器,接收所有 HTTP 请求并分发
- 作用:统一入口点,初始化 WebApplicationContext
HandlerMapping
- 功能:定义规则决定哪个处理器处理特定请求
- 作用:将请求映射到具体的处理器
HandlerAdapter
- 功能:适配器接口,执行映射到的处理器
- 作用:使不同类型处理器可以被一致调用
ViewResolver
- 功能:根据逻辑视图名找到实际视图资源
- 作用:解耦视图名称和物理位置
Controller
- 功能:接收请求并调用业务逻辑
- 作用:处理用户输入,准备数据,选择视图
ModelAndView
- 功能:包含模型数据和逻辑视图名
- 作用:连接业务逻辑层与表示层
View
- 功能:将模型数据转换为最终用户界面
- 作用:数据转换及数据渲染
Interceptor
- 功能:在请求到达控制器前后执行操作
- 作用:实现日志记录、权限检查等功能
ExceptionHandler
- 功能:全局捕获和处理控制器抛出的异常
- 作用:集中处理错误,简化代码结构
1.4 SpringMVC 之 Helloworld(入门体验)
1.4.1 环境准备
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>1.4.2 定义请求处理器 Handler(Controller)
java
package com.at.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller("helloController")
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello springMVC!!!";
}
}1.4.3 启动及测试项目
1.5 @RequestMapping 详解
@RequestMapping 是 Spring MVC 中用于映射 Web 请求到处理方法的注解。它可以应用于类或方法级别,用来指定 URL 模式、HTTP 方法类型等条件。
1.5.1 @RequestMapping 位置
- 类级别:为当前类映射 URL(不能单独使用)
java
@Controller
@RequestMapping("/users")
public class UserController {
// ...
}- 方法级别:为当前方法映射 URL(可以单独使用)
java
@RequestMapping(value = "/getUser", method = RequestMethod.GET)
@ResponseBody
public String getUser() {
return "success";
}1.5.2 @RequestMapping 路径匹配
- 精确匹配
java
@RequestMapping("/hello")
@RequestMapping("/helloController/hello")- 模糊匹配
java
@RequestMapping("/hello/?") // ?: 任意单个字符
@RequestMapping("/helloController/*") // *: 任意数量任意字符(单层)
@RequestMapping("/helloController/**") // **: 任意层目录的任意数量字符1.5.3 @RequestMapping 常用属性
- value:指定请求的 URL 模式
- method:指定允许的 HTTP 方法(GET、POST、PUT、DELETE 等)
- params:限定特定的请求参数存在时才匹配
- headers:限定特定的请求头存在时才匹配
- consumes:限定请求的内容类型匹配
- produces:限定响应的内容类型匹配
1.5.4 @RequestMapping 四大组合注解
Spring 提供了一些更具体的注解,它们是
@RequestMapping的简化版本:
@GetMapping:组合了@RequestMapping(method = RequestMethod.GET)@PostMapping:组合了@RequestMapping(method = RequestMethod.POST)@PutMapping:组合了@RequestMapping(method = RequestMethod.PUT)@DeleteMapping:组合了@RequestMapping(method = RequestMethod.DELETE)@PatchMapping:组合了@RequestMapping(method = RequestMethod.PATCH)
第 2 章 SpringMVC 处理请求数据
2.1 接收查询&请求体参数
2.1.1 默认情况:参数名与形参名一致
SpringMVC 中形参列表可直接接收请求参数
- 参数名与形参列表中参数名必须一致
- 如不一致:默认值 null
- Controller 接收数据
java
@ResponseBody
@GetMapping("/doRequestParameter")
public String doRequestParameter(String stuName, Integer stuAge){
System.out.println("stuName = " + stuName);
System.out.println("stuAge = " + stuAge);
return "success";
}2.1.2 @RequestParam 注解
参数名与形参名不一致时,使用@RequestParam 注解获取请求参数
常用属性:
- value:请求参数的名称
- required:参数是否必需(默认 true)
- defaultValue:未提供参数时的默认值
- name:value 的别名
Controller 接收数据
java
@ResponseBody
@GetMapping("/doRequestParameter")
public String doRequestParameter(String stuName,
@RequestParam(value = "stuAge2", required = false) Integer stuAge){
System.out.println("stuName = " + stuName);
System.out.println("stuAge = " + stuAge);
return "success";
}2.1.3 POJO 入参
SpringMVC 支持 POJO 入参,将 POJO 对象设置为形参即可
- 参数名与 POJO 中的属性名必须一致
- 如不一致会注入 null 值
- POJO 实体类
java
package com.at.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer stuId;
private String stuName;
private Integer stuAge;
private String[] hobbys;
}- Controller 接收数据
java
@ResponseBody
@PostMapping("/testPojoParam")
public String testPojoParam(Student student){
System.out.println("student = " + student);
return "success";
}2.2 接收路径参数(@PathVariable)
SpringMVC 支持在 URL 中使用占位符入参,常用于 RESTFul 风格
- 占位符语法:
- @PathVariable("参数名")
- Controller 接收数据
java
@ResponseBody
@GetMapping("/testURLParam/{stuId}")
public String testURLParam(@PathVariable("stuId") Integer stuId){
System.out.println("stuId = " + stuId);
return "success";
}2.3 接收 JSON 参数
SpringMVC 使用@RequestBody 注解接收 Json 数据
- 使用 String 类型接收:获取 Json 字符串
- 使用 POJO 类型接收:获取 POJO 对象
- Controller 接收数据
java
@ResponseBody
@PostMapping("/testJSONParam")
public String testJSONParam(@RequestBody Student student /*String jsonStr*/){
// System.out.println("jsonStr = " + jsonStr); // json字符串
System.out.println("student = " + student);
return "success";
}2.4 接收 Cookie 数据
SpringMVC 使用@CookieValue 注解获取 Cookie 数据
- Controller 接收数据
java
@ResponseBody
@GetMapping("/testCookieInfo")
public String testCookieInfo(@CookieValue("JSESSIONID") String jsessionId){
System.out.println("jsessionId = " + jsessionId);
return "success";
}2.5 接收请求头数据
SpringMVC 使用@RequestHeader 注解接收请求头信息
- Controller 接收数据
java
@ResponseBody
@GetMapping("/testRequestHeaderInfo")
public String testRequestHeaderInfo(@RequestHeader("User-Agent") String userAgent){
System.out.println("User-Agent = " + userAgent);
return "success";
}2.6 接收文件数据(文件上传)
SpringBoot 环境中自带文件上传处理器,可在 application.properties 中配置
properties
spring.application.name=spring09-mvc
# 总文件大小限定
spring.servlet.multipart.max-request-size=200MB
# 单文件大小限定
spring.servlet.multipart.max-file-size=20MB
# 文件数量限定
spring.webflux.multipart.max-parts=10- HTML 代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>文件上传</title>
</head>
<body>
<!--
文件上传表单要求:
1. method必须是Post
2. enctype必须是multipart/form-data
3. SpringMVC默认限定:单个文件1M以内,总文件10M以内
-->
<form method="post" enctype="multipart/form-data" action="file/upload">
用户名 <input type="text" name="username" /> <br />
头像 <input type="file" name="headImg" /> <br />
生活照 <input type="file" name="lifeImg" multiple="multiple" /> <br />
<input type="submit" value="上传" />
</form>
</body>
</html>- Controller 处理文件上传
java
package com.at.spring09mvc.controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.UUID;
@RequestMapping("/file")
@RestController
public class FileController {
@RequestMapping("/upload")
public String upload(
@RequestParam("username") String username,
@RequestParam("headImg") MultipartFile headImg,
@RequestParam("lifeImg") MultipartFile[] lifeImg) throws Exception {
// 文件名处理
String originalFilename = headImg.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
String newFileName = UUID.randomUUID().toString() + suffix;
// 保存到指定目录
File classPath = ResourceUtils.getFile("classpath:static/");
File dir = new File(classPath, "upload");
if (!dir.exists()) {
dir.mkdirs();
}
headImg.transferTo(new File(dir, newFileName));
return "success";
}
}2.7 原生 ServletAPI 入参
SpringMVC 支持原生 ServletAPI,直接作为形参入参即可
java
@ResponseBody
@PostMapping("/testServletAPIInfo")
public String testServletAPIInfo(HttpServletRequest request, HttpServletResponse response){
HttpSession session = request.getSession();
String stuId = request.getParameter("stuId");
System.out.println("stuId = " + stuId);
String stuName = request.getParameter("stuName");
System.out.println("stuName = " + stuName);
return "success";
}2.8 接收 HttpEntity 获取所有请求信息(了解)
SpringMVC 可以使用 HttpEntity 获取请求信息,包括请求头、请求体等
java
package com.at.spring09mvc.controller;
import com.at.spring09mvc.pojo.Product;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/httpEntity")
@RestController
public class HttpEntityController {
@RequestMapping("/test1")
public String test1(HttpEntity<Product> entity) {
System.out.println(entity.getHeaders());
System.out.println(entity.getBody());
return "test1";
}
}第 3 章 SpringMVC 处理响应数据
3.1 响应数据
SpringMVC 使用@ResponseBody 注解响应数据,包括普通文本或 Json 数据
@ResponseBody 书写位置:
- 方法上:表示当前方法响应数据
- 类上:表示当前类中所有方法响应数据
- @RestController = @Controller + @ResponseBody
3.1.1 普通文本
java
@ResponseBody
@RequestMapping("/testJsonData")
public String doJsonDataString(){
return "success";
}3.1.2 Json 数据
java
@ResponseBody
@RequestMapping("/testJsonData")
public Student doJsonDataString(){
Student student = new Student();
student.setStuId(1005);
student.setStuName("wangwu");
student.setStuAge(18);
student.setHobbys(new String[]{"打游戏","看电影"});
return student;
}3.2 响应静态资源
3.2.1 转发与重定向(html)
- 转发
java
@RequestMapping("/testStaticResource")
public String doStaticResource(){
System.out.println("转发=======");
return "/html/index.html"; // 默认转发
// return "forward:/html/index.html"; // 显式转发
}- 重定向
java
@RequestMapping("/testStaticResource")
public String doStaticResource(){
System.out.println("重定向=======");
return "redirect:/html/index.html"; // 重定向
}3.2.2 其他资源(了解)
java
@RequestMapping("/testStaticResource")
public String doStaticResource(){
System.out.println("其他静态资源=======");
return "/imgs/a.jpg";
}3.3 响应文件(文件下载)
- HTML 代码
html
<a href="fileController/filedownload?fileName=3.jpg">3.jpg下载</a>- Controller 下载
java
@RequestMapping("/filedownload")
public ResponseEntity<byte[]> filedownload(String fileName) throws Exception {
File staticDir = ResourceUtils.getFile("classpath:static/path/"+fileName);
InputStream inputStream = new FileInputStream(staticDir);
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes);
// 响应头设置
MultiValueMap responseHeaderMap = new HttpHeaders();
responseHeaderMap.add("Content-Disposition", "attachment; filename="+fileName);
ResponseEntity responseEntity = new ResponseEntity(bytes, responseHeaderMap, HttpStatus.OK);
return responseEntity;
}第 4 章 RESTFul 风格设计及练习
4.1 RESTFul 风格概述
4.1.1 RESTFul 简介
RESTful(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议。
4.1.2 RESTFul 特点
- 每一个 URI 代表 1 种资源
- 客户端使用 GET、POST、PUT、DELETE 对服务端资源进行操作
- 资源的表现形式是 XML 或者 JSON
- 客户端与服务端之间的交互在请求之间是无状态的
4.2 RESTFul 设计规范
4.2.1 HTTP 协议请求方式规范
| 操作 | 请求方式 |
|---|---|
| 查询操作 | GET |
| 保存操作 | POST |
| 删除操作 | DELETE |
| 更新操作 | PUT |
4.2.2 RESTFul 与传统风格对比
| 传统风格 URL | 传统方式请求方式 | RESTFul 风格 URL | RESTFul 请求方式 | |
|---|---|---|---|---|
| 增加 | /saveEmp | POST | /emp | POST |
| 删除 | /deleteEmp?id=1001 | GET | /emp/101 | DELETE |
| 修改 | /updateEmp | POST | /emp | PUT |
| 查询 | /getEmpById?id=1001 | GET | /emp/101 | GET |
4.3 RESTFul 实战案例
4.3.1 准备环境
- 数据库环境
sql
CREATE TABLE t_emp(
eid INT PRIMARY KEY AUTO_INCREMENT,
ename VARCHAR(20) NOT NULL,
salary DOUBLE(10,2) NOT NULL,
address VARCHAR(20) NOT NULL,
did INT NOT NULL
);
INSERT INTO t_emp(ename,salary,address,did) VALUES('zhangsan',15000,'北京昌平',1);
INSERT INTO t_emp(ename,salary,address,did) VALUES('lisi',15000,'北京朝阳',1);
INSERT INTO t_emp(ename,salary,address,did) VALUES('王五',16000,'北京昌平',2);
INSERT INTO t_emp(ename,salary,address,did) VALUES('zhaoliu',20000,'北京昌平',1);
INSERT INTO t_emp(ename,salary,address,did) VALUES('qianqi',25000,'北京昌平',2);4.3.2 参考案例代码
- pom.xml
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>- application.properties
properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=root- POJO 实体层
java
package com.at.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer eid;
private String ename;
private Double salary;
private String address;
private Integer did;
private Dept dept;
}- DAO 持久化层
java
package com.at.dao;
import com.at.pojo.Employee;
import java.util.List;
public interface EmployeeDao {
void insertEmployee(Employee employee);
void deleteEmployeeById(Integer id);
void updateEmployee(Employee employee);
Employee getEmployeeById(Integer id);
List<Employee> getAllEmps();
List<Employee> getAllEmpAndDept();
}java
package com.at.dao.impl;
import com.at.dao.EmployeeDao;
import com.at.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("employeeDao")
public class EmployeeDaoImpl implements EmployeeDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void insertEmployee(Employee employee) {
String sql = "INSERT INTO t_emp(ename,salary,address,did) VALUES(?,?,?,?)";
jdbcTemplate.update(sql, employee.getEname(), employee.getSalary(),
employee.getAddress(), employee.getDid());
}
// 其他方法实现...
}- Service 业务逻辑层
java
package com.at.service;
import com.at.pojo.Employee;
import java.util.List;
public interface EmployeeService {
void saveEmployee(Employee employee);
void deleteEmployeeById(Integer id);
void updateEmployee(Employee employee);
Employee findEmployeeById(Integer id);
List<Employee> findAllEmps();
List<Employee> getAllEmpAndDept();
}java
package com.at.service.impl;
import com.at.dao.EmployeeDao;
import com.at.pojo.Employee;
import com.at.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("employeeService")
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeDao employeeDao;
@Override
public void saveEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}
// 其他方法实现...
}- Controller 控制层
java
package com.at.controller;
import com.at.pojo.Employee;
import com.at.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping("/employees")
public String addEmployee(@RequestBody Employee employee) {
employeeService.saveEmployee(employee);
return "{'status':'ok'}";
}
@DeleteMapping("/employee/{eid}")
public String deleteEmployeeById(@PathVariable("eid") Integer id) {
employeeService.deleteEmployeeById(id);
return "{'status':'ok'}";
}
@PutMapping("/employee/all")
public String updateEmployee(@RequestBody Employee employee) {
employeeService.updateEmployee(employee);
return "{'status':'ok'}";
}
@GetMapping("/employee/{eid}")
public Employee getEmpById(@PathVariable("eid") Integer eid){
return employeeService.findEmployeeById(eid);
}
@GetMapping("/employee")
public List<Employee> getAllEmp(){
return employeeService.findAllEmps();
}
@GetMapping("/empAndDept")
public List<Employee> getAllEmpAndDept(){
return employeeService.getAllEmpAndDept();
}
}4.4 跨域问题
4.4.1 跨域问题概述
- 什么是跨域:客户端脚本尝试访问与当前页面不在同一个域下的资源
- 跨域的原因:浏览器同源策略防止 CSRF 攻击
4.4.2 跨域问题解决方案
主要使用 CORS 解决方案
- 配置类方式(了解)
java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);
}
}- 注解方式:@CrossOrigin
java
@CrossOrigin(origins = "https://example.com")
@RestController
@RequestMapping("/api")
public class MyController {
// 所有方法允许来自 https://example.com 的请求
}java
@RestController
@RequestMapping("/api")
public class MyController {
@CrossOrigin(origins = "https://example.com", methods = {RequestMethod.GET, RequestMethod.POST})
@GetMapping("/data")
public ResponseEntity<String> getData() {
return new ResponseEntity<>("Some data", HttpStatus.OK);
}
}第 5 章 SpringMVC 异常处理器
5.1 异常处理基本概念
为什么需要处理异常?
- 提高用户体验,友好错误提示
- 增强系统的可靠性,防止未处理异常传播
- 简化调试与维护,减少重复代码
如何处理异常?
- 编程式异常处理:耦合度高(不推荐)
- 声明式异常处理:使用切面(推荐)
5.2 声明式异常处理
使用注解实现异常处理器:
@ControllerAdvice:定义全局异常处理器@ExceptionHandler:处理特定类型异常
5.2.1 全局异常处理
java
package com.at.myexceptionhandler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class MyExceptionHandler {
@ResponseBody
@ExceptionHandler(NullPointerException.class)
public String doNullPointerException(Exception ex) {
System.out.println("ex = " + ex);
return "{'status:'501'}"; // 响应文本提示
}
@ExceptionHandler(ArithmeticException.class)
public String doArithmeticException(Exception ex) {
System.out.println("ex = " + ex);
return "/error/error_502.html"; // 响应页面提示
}
}5.2.2 局部异常处理(了解)
java
@Controller
public class MyController {
@ExceptionHandler(ResourceNotFoundException.class)
public String handleResourceNotFoundException(ResourceNotFoundException ex, Model model) {
model.addAttribute("errorMessage", ex.getMessage());
return "error/resource-not-found";
}
}第 6 章 SpringMVC 拦截器
6.1 拦截器(Interceptor)简介
拦截器用于在请求处理的不同阶段插入自定义逻辑:
- 预处理请求:在请求传递给控制器之前执行
- 后处理响应:在控制器方法执行完毕但视图渲染之前执行
- 完成处理:在整个请求处理完成后执行
6.2 拦截器基本实现
6.2.1 定义拦截器
java
package com.at.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component("myInterceptor1")
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("==>1.Myinterceprot111->preHandle()!!!");
return true; // true:放行 false:不放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("==>3.Myinterceprot111->postHandle()!!!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("==>4.Myinterceprot111->afterCompletion()!!!");
}
}6.2.2 注册拦截器
java
package com.at.config;
import com.at.interceptor.MyInterceptor1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor1 myInterceptor1;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor1)
.addPathPatterns("/interceptorController/testInterceptor");
}
}6.3 拦截器工作原理
6.3.1 单个拦截器工作原理
- 客户端发送请求
- 执行 preHandle(),放行请求(return true)
- 执行 Controller 方法
- 执行 postHandle()
- 执行 afterCompletion()
6.3.2 多个拦截器工作原理
假设三个拦截器按 A -> B -> C 顺序注册:
java
A - Pre Handle
B - Pre Handle
C - Pre Handle
// Controller 执行...
C - Post Handle
B - Post Handle
A - Post Handle
// 视图渲染完成...
C - After Completion
B - After Completion
A - After Completion6.4 拦截器与过滤器异同
| 特性 | 拦截器(Interceptor) | 过滤器(Filter) |
|---|---|---|
| 作用范围 | 仅限于 Spring MVC 控制器请求 | 整个 Web 应用程序中的所有请求/响应 |
| 生命周期 | 依赖于 Spring 容器 | 随应用启动而初始化 |
| 配置方式 | Java 配置类或 XML | web.xml 或注解 |
| 执行顺序 | preHandle()按注册顺序,postHandle()和 afterCompletion()逆序 | 按配置顺序依次执行 |
| 功能特性 | 细粒度控制,认证授权、性能监控等 | 字符编码设置、日志记录、权限检查等 |
| 性能考虑 | 只针对 Spring MVC 请求,性能更好 | 拦截所有请求,可能引入额外开销 |
| 适用场景 | 处理 Spring MVC 控制器管理的请求 | 需要对所有 HTTP 请求进行统一处理 |
第 7 章 SpringMVC 数据校验
7.1 SpringMVC 数据校验概念
使用 Bean Validation API 确保接收到的数据符合预期格式和规则
常用校验规则:
| 校验注解 | 作用 |
|---|---|
| @AssertFalse | 验证 Boolean 类型字段是否为 false |
| @AssertTrue | 验证 Boolean 类型字段是否为 true |
| @DecimalMax | 验证数字是否小于等于指定最大值 |
| @DecimalMin | 验证数字是否大于等于指定最小值 |
| @Digits(integer, fraction) | 验证数值格式 |
| 验证字符串是否为邮箱地址格式 | |
| @Future | 验证日期是否在当前时间之后 |
| @Past | 验证日期是否在当前时间之前 |
| @Min(value) | 验证数字是否大于等于指定最小值 |
| @Max(value) | 验证数字是否小于等于指定最大值 |
| @Null | 验证对象是否为 null |
| @NotNull | 验证对象是否不为 null |
| @NotEmpty | 验证字符串是否非空 |
| @NotBlank | 验证字符串是否非空白字符 |
| @Size(max=, min=) | 验证字符串、集合、Map、数组的大小 |
| @Pattern(regexp=, flag=) | 验证字符串是否符合指定正则表达式 |
7.2 SpringMVC 数据校验基本实现
7.2.1 导入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>7.2.2 定义校验规则
java
package com.at.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {
@NotNull(message = "id不能为空!!!")
private Integer stuId;
@Length(min = 3, max = 6, message = "长度在3-6之间!!!")
private String stuName;
@Min(value = 18, message = "年龄最小18岁!!!")
@Max(value = 120, message = "年龄最大120岁!!!")
private Integer stuAge;
@Email(message = "邮箱格式不正确!!!")
private String stuEmail;
}7.2.3 控制器中验证
java
package com.at.controller;
import com.at.pojo.Student;
import com.at.pojo.StudentDTO;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class TestValidateController {
@GetMapping("/testValidate")
public String doValidate(@RequestBody @Valid StudentDTO studentDTO,
BindingResult br) {
if(br.hasErrors()){
Map<String,Object> errorMap = new HashMap<>();
List<FieldError> fieldErrors = br.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
}
return errorMap.toString();
}
Student student = new Student();
BeanUtils.copyProperties(studentDTO, student);
System.out.println("student = " + student);
return "doValidate";
}
}7.3 SpringMVC 自定义数据校验器(了解)
7.3.1 定义校验注解
java
package com.at.annotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = {GenderValidate.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Gender {
String message() default "性别只能是男或女!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}7.3.2 定义校验注解规则
java
package com.at.annotation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class GenderValidate implements ConstraintValidator<Gender,String> {
@Override
public void initialize(Gender constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value.equals("男") || value.equals("女");
}
}java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {
// 其他属性...
@Gender(message = "请输入正确的性别!")
private String stuGender;
}7.4 VO 与 DTO
7.4.1 各种 O 的概念
- Value Object (VO):强调不可变性和基于内容的相等性
- Data Transfer Object (DTO):简化不同层次间的数据传输
- Entity:表示持久化的业务实体
- Form Object:专为表单提交设计
- Command Object:封装执行某项操作所需的所有信息
- Transfer Object (TO):专注于远程调用中的高效数据传输
7.4.2 DTO 介绍
DTO 全称:Data Transfer Object
- 定义:在不同应用程序层之间传递数据的简单 POJO
- 使用场景:
- 简化复杂业务逻辑与视图之间的数据交换
- 避免直接暴露实体类给前端
- 支持多样的展示需求
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentDTO {
@NotNull(message = "id不能为空!!!")
private Integer stuId;
@Length(min = 3, max = 6, message = "长度在3-6之间!!!")
private String stuName;
// 其他属性和校验规则...
}7.4.3 VO 介绍
VO 全称:Value Object
- 定义:表示值的对象,状态不可改变
- 使用场景:
- 需要确保对象状态不会发生变化时
- 领域驱动设计(DDD)中封装特定含义的值
java
public final class Money {
private final BigDecimal amount;
private final Currency currency;
public Money(BigDecimal amount, Currency currency) {
this.amount = amount;
this.currency = currency;
}
// Getters but no setters...
}第 8 章 Swagger 接口文档
8.1 Swagger 介绍
Swagger 是一个用于设计、构建、记录和使用 RESTful Web 服务的开源框架,遵循 OpenAPI 规范。Knife4j 是基于 Swagger 的增强套件。
8.2 Swagger 基本应用
8.2.1 常用 API
| 注解 | 标注位置 | 作用 |
|---|---|---|
| @Tag | controller 类 | 描述 controller 作用 |
| @Parameter | 参数 | 标识参数作用 |
| @Parameters | 参数 | 参数多重说明 |
| @Schema | model 层的 JavaBean | 描述模型作用及属性 |
| @Operation | 方法 | 描述方法作用 |
| @ApiResponse | 方法 | 描述响应状态码等 |
8.2.2 实现步骤
- 导入依赖
xml
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>- 编写配置文件
yaml
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs
group-configs:
- group: "default"
paths-to-match: "/**"
packages-to-scan: com.at.controller
knife4j:
enable: true
setting:
language: zh_cn- 使用注解
java
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Employee implements Serializable {
@Schema(description = "修改员工信息时,该属性必须有值.增加员工信息时则不需要赋值")
private Integer id;
@Schema(description = "员工姓名")
private String name;
// 其他属性...
}java
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@RestController
@RequestMapping("/api/v1")
@Tag(name = "员工管理", description = "员工信息管理的控制器")
public class UserController {
@Operation(summary = "查询全部员工信息的处理器")
@GetMapping("/employees")
public String getEmployees() {
List<Employee> employees = employeeService.findAllEmployee();
return employees.toString();
}
// 其他方法...
}- 访问 Swagger UI:
http://ip:port/doc.html
第 9 章 SpringMVC 工作原理
9.1 SpringMVC 九大组件
9.1.1 DispatcherServlet
- 作用:前端控制器,控制中心
- 核心调度方法:doDispatch()
9.1.2 HandlerMapping
- 作用:处理器映射器,根据请求找到 Handler
- 通过 HandlerMapping 获取 HandlerAdapter 对象
9.1.3 HandlerAdapter
- 作用:处理器适配器,按照规则执行 Handler
- 通过 HandlerAdapter 调用 Controller 方法
9.1.4 Handler(Controller)
- 作用:处理器,负责处理业务逻辑
- 注解:@Controller
9.1.5 ModelAndView
- 作用:封装 Model 和 View 信息
9.1.6 ViewResolver
- 作用:视图解析器,解析逻辑视图名
9.1.7 View
- 作用:视图,负责将结果显示给用户
9.1.8 ExceptionHandler
- 作用:异常处理器
- 注解:@ExceptionHandler
9.1.9 Interceptor
- 作用:拦截器,预处理和后处理操作