接收参数的方式
参数格式是 k=v [常见于get请求 或者 文件上传等请求], 直接接收或者 pojo实体接收
- 直接接收或者
@RequestParam重命名接收
// 前端请求格式为
/user?id=1&name=bwf&age=88
// 当k与变量名一致时,可以不用@RequestParam,同名变量接收
@GetMapping("/user")
public String getUser(
@RequestParam("id") Long userId, // 必须参数 取出id赋值给变量 userId
@RequestParam(value = "name", required = false) String name, // 可选参数
@RequestParam(value = "age", defaultValue = "18") int age // 默认值
) {
return "user";
}
- 封装为POJO对象接收
// 前端请求格式为
/user?username=bwf&email=163@qq.com&age=88
@GetMapping("/register")
public String register(UserForm form) {
// Spring 自动将参数绑定到对象的同名属性
return "success";
}
// 表单对象
@Data
public class UserForm {
private String username;
private String email;
private Integer age;
}
参数格式请求体json格式 [常见于post等请求]
@RequestBody
@PostMapping("/order")
public void createOrder(@RequestBody OrderRequest request) {
}
@Data
public class OrderRequest {
private Long userId;
private List<OrderItemDTO> items;
private AddressDTO shippingAddress;
private String paymentMethod;
}
文件上传 MultipartFile类接收
/**
* 前端上传图片
* h5请求方式
* post
* h5 入参
* let formData = new FormData();
* formData.append("file", file.raw);
* formData.append("file", file.raw);
* formData.append("attach", file.raw);
* formData.append(其他字段名, 字段对应的值)
* h5 请求头
* {headers:{"Content-Type":"multipart/form-data"}}
* h5发送
* {headers:{"Content-Type":"multipart/form-data"}}
* data:formData
*/
@PostMapping("/upload")
public void upload(User user, @RequestParam("file")MultipartFile[] files, @RequestParam("attach") MultipartFile attach){
log.info("upload---user:{}", user); // 其他字段名映射到User实体类中
}
文件下载
- 图片/excel下载 (前端get/post请求均可)
/**
* 下载图片等文件
* @param httpServletResponse
*/
public void downLoadFile(HttpServletResponse httpServletResponse) {
//模拟读取 resources目录下的资源
String filePath = this.getClass().getResource("/static/pic/1.jpg").getPath();
File file = new File(filePath);
String fileName = filePath.substring(filePath.lastIndexOf("/")+1);
try {
InputStream inputStream = Files.newInputStream(file.toPath());
//响应头需要设置内容的处理方式:下载文件需要指定为文件
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName);
httpServletResponse.setHeader("Content-type","blob");
cn.hutool.core.io.IoUtil.copy(inputStream, httpServletResponse.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
//excel下载
public void exportExcel(HttpServletResponse httpServletResponse){
List<User> list = "下载的集合"
httpServletResponse.setContentType("application/vnd.ms-excel");
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
httpServletResponse.setCharacterEncoding("UTF-8");
com.alibaba.excel.EasyExcel.write(httpServletResponse.getOutputStream, User.class).sheet("sheet1").doWrite(list)
}
@Data
@AllArgsConstructor
public class User{
/**
* value={"一级表头","二级表头","三级表头"}
* index=0 排序从左到右
*/
@ExcelProperty(value={"用户信息","姓名"},index=0)
private String userName;
@ExcelProperty(value={"用户信息","性别"},index=1)
private String sex;
/**
* 不要在excel中展示
*/
@ExcelIgnore
private String score
}
封装响应统一结果类
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class Result<T>{
private String code;
private String msg;
private T data;
public Result(String code, String msg) {
this.code = code;
this.msg = msg;
}
public static Result ok(){
return new Result("200", "OK");
}
public static<T> Result<T> ok(T data){
return new Result<>("200", "ok", data);
}
}
取前端路径参数
@PathVariable
@GetMapping("/users/{id}")
public void getUserById(@PathVariable Long id) {
// 访问: GET /users/123
// id = 123
}
@GetMapping("/products/{productId}")
public void getProduct(
@PathVariable("productId") Long id // 参数名与路径变量名不同
) {
// 访问: GET /products/789
// id = 789
}
springmvc 拦截器
-
- 实现
HandlerInterceptor接口生成一个拦截器
- 实现
-
- 实现
WebMvcConfigurer接口addInterceptors方法添加上述拦截器
- 实现
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {
/**
* @return boolean true-放行请求 false-拦截请求
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
log.info("AuthInterceptor.preHandle---start");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
}
}
@Configuration
public class MySpringConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
}
}
拦截器执行顺序
- 正常执行顺序
请求进入 →
拦截器1.preHandle() ✅ (放行)
拦截器2.preHandle() ✅ (放行)
拦截器3.preHandle() ✅ (放行)
↓
Controller执行
↓
拦截器3.postHandle() ← (逆序)
拦截器2.postHandle() ← (逆序)
拦截器1.postHandle() ← (逆序)
↓
视图渲染
↓
拦截器3.afterCompletion() ← (逆序)
拦截器2.afterCompletion() ← (逆序)
拦截器1.afterCompletion() ← (逆序)
↓
响应返回
- 中断执行顺序(拦截器2不放行)
请求进入 →
拦截器1.preHandle() ✅ (放行)
拦截器2.preHandle() ❌ (返回false,拦截!)
↓
【中断!】后续拦截器3和Controller不会执行
↓
拦截器1.afterCompletion() ← (只有放行的才执行)
↓
直接返回响应给客户端
Spring MVC 拦截器 vs 过滤器(Filter)
| 对比维度 | Filter(过滤器) | Interceptor(拦截器) |
|---|---|---|
| 所属规范 | Servlet 规范(J2EE) | Spring MVC 框架 |
| 依赖 | 依赖 Servlet 容器(Tomcat) | 依赖 Spring 框架 |
| 位置 | 在 DispatcherServlet 之前 | 在 DispatcherServlet 之后,Controller 前后 |
| 作用范围 | 对所有请求有效 | 只对 Spring MVC 处理的请求有效 |
| 放行 | chain.doFilter() 放行请求 | preHandle返回true 放行请求 |
| 使用场景 | 字符编码、CORS、安全过滤、日志记录 | 权限验证、日志记录、性能监控、数据预处理 |
| 配置方式 | web.xml 或 @WebFilter 注解 | 实现 WebMvcConfigurer 接口 |
| 执行顺序 | 通过 @Order 或 web.xml 配置顺序 | 通过 order() 方法配置顺序 |
异常拦截器@RestControllerAdvice + @ExceptionHandler
@Slf4j
@RestControllerAdvice
public class GloabalAdvice {
@ExceptionHandler(Exception.class)
public Result exceptionHandler(Exception e){
log.info("系统异常:{}", e);
return new Result("500", "系统发生错误");
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e){
log.info("入参校验不通过---e:{}", e);
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
HashMap<String, String> fieldErrorMap = new HashMap<>();
for (FieldError fieldError : fieldErrors) {
String field = fieldError.getField();
String defaultMessage = fieldError.getDefaultMessage();
fieldErrorMap.put(field, defaultMessage);
}
return new Result<>("500", "入参校验不通过", fieldErrorMap);
}
@ExceptionHandler(BizException.class)
public Result bizExceptionHandler(BizException e){
log.info("业务异常:{}", e);
return new Result(e.getCode(), e.getMsg());
}
}
校验入参
- 引入包
spring-boot-starter-validation
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- Controller层开启校验
@Valid
@PostMapping("/user/update")
public Result<User> updateUser(@RequestBody @Valid User user){
log.info("updateUser---user:{}", user);
if(!StringUtils.hasText(user.getId())){
throw new BizException(ExceptionEnum.USER_NOT_FOUND);
}
return Result.ok(user);
}
- 添加校验规则
@Data
public class User {
@NotNull(message = "用户id不能为空")
private String id;
@NotNull(message = "年龄不能为空")
@Min(value = 0, message = "最小年龄为0")
@Max(value = 150, message = "最大年龄为150")
private Integer age;
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
private String username;
@NotNull(message = "身份证号码不能为空")
@IdCard(message = "身份证号码格式不正确")
private String idCard;
@Valid
@NotNull(message = "地址不能为空")
private Address address;
}
- 校验不通过,默认不会进入Controller。可以被全局异常拦截器捕获,错误类型
MethodArgumentNotValidException
常用校验注解大全
- 空值检查
// 1. @NotNull - 不能为null(但可以为空字符串)
@NotNull(message = "ID不能为空")
private Long id;
// 2. @NotEmpty - 集合/数组/Map/字符串不能为空
@NotEmpty(message = "角色列表不能为空")
private List<String> roles;
@NotEmpty(message = "用户名不能为空")
private String username; // 不能是null或空字符串""
// 3. @NotBlank - 字符串不能为null且必须包含非空白字符
@NotBlank(message = "密码不能为空")
private String password; // 不能是null、""、" "
- 数值范围检查
// 1. @Min - 最小值限制
@Min(value = 0, message = "年龄不能小于0")
private Integer age;
@Min(value = 1, message = "数量必须大于0")
private BigDecimal quantity;
// 2. @Max - 最大值限制
@Max(value = 150, message = "年龄不能大于150")
private Integer age;
// 3. @Range - 数值范围(Spring扩展)
@Range(min = 0, max = 100, message = "分数必须在0-100之间")
private Integer score;
- 字符串长度检查
// 1. @Size - 集合/数组/字符串长度
@Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
private String password;
@Size(min = 1, max = 10, message = "最多选择10个标签")
private List<String> tags;
// 2. @Length - 字符串长度(Spring扩展)
@Length(min = 2, max = 10, message = "名称长度必须在2-10之间")
private String name;
- 模式匹配检查
// 1. @Pattern - 正则表达式匹配
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
private String username;
@Pattern(regexp = "^1[3-9]\d{9}$", message = "手机号格式不正确")
private String phone;
-
自定义校验器 + 注解
注解 @IdCard
validatedBy的值为自定义校验器
@Documented
@Constraint(
validatedBy = {IdCardValidator.class}
)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdCard {
String message() default "{jakarta.validation.constraints.NotBlank.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
校验器 IdCardValidator 实现 ConstraintValidator接口的isValid方法
/**
* ConstraintValidator 泛型1:注解名称 泛型2:注解所作用的属性类型
*
*/
@Slf4j
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
/**
* 是否有效
* @param idCard 校验目标的属性值
* @param constraintValidatorContext
* @return boolean true-校验通过 false-校验失败
*/
@Override
public boolean isValid(String idCard, ConstraintValidatorContext constraintValidatorContext) {
log.info("IdCardValidator---start:{}", idCard);
if (idCard == null) {
return true;
}
// 简单的身份证格式校验
String regex = "(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)";
if (!idCard.matches(regex)) {
return false;
}
return true;
}
}
自定义业务异常类
BizException
@NoArgsConstructor
@AllArgsConstructor
@Data
public class BizException extends RuntimeException{
private String code;
private String msg;
public BizException(ExceptionEnum exceptionEnum){
this.code = exceptionEnum.getCode();
this.msg = exceptionEnum.getMsg();
}
}
- 业务错误常见枚举
ExceptionEnum
import lombok.Getter;
public enum ExceptionEnum {
USER_NOT_FOUND("1000", "用户不存在");
@Getter
private String code;
@Getter
private String msg;
ExceptionEnum(String code, String msg){
this.code = code;
this.msg = msg;
}
}
- 使用
throw new BizException(ExceptionEnum.USER_NOT_FOUND);
VO / DTO / BO / Entity
┌─────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (Controller / Resource / Endpoint) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Request │ │ Response │ │
│ │ VO │ │ VO │ │
│ └─────────────────┘ └─────────────────┘ │
└───────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Business Layer │
│ (Service / Manager / Facade) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ BO │ │ BO │ │
│ │ (Business │ │ (Business │ │
│ │ Object) │ │ Object) │ │
│ └─────────────────┘ └─────────────────┘ │
└───────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Persistence Layer │
│ (Repository / DAO / Mapper) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Entity │ │ Entity │ │
│ │ (DO/PO) │ │ (DO/PO) │ │
│ └─────────────────┘ └─────────────────┘ │
└───────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Database Layer │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Table │ │ Table │ │
│ │ Schema │ │ Schema │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
- VO (View Object 视图对象) 用于Controller 接收前端入参 + 返回给前端
- DTO (Data Transfer Object 数据传输对象) 用于Controller-Service或Service-Service之间的数据传输
- BO (Business Object 业务对象) 核心业务逻辑载体
- Entity 与数据库表一一对应
knife4j Api文档
doc.xiaominfo.com/v2/document…
- 加入依赖
<dependency>
<groupId>com.github.xingfudeshi</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.6.0</version>
</dependency>
- application.yml配置 需要将
packages-to-scan值改为controller所在位置
# springdoc-openapi项目配置
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: org.example.springmvc.controller
# knife4j的增强配置,不需要增强可以不配
knife4j:
enable: true
setting:
language: zh_cn
- 加接口文档注解
@Tag标注于Controller类@OperationController类中方法@Schema入参bean属性 - 访问接口文档 http://localhost:8080/doc.html#/