news 2026/4/3 4:56:58

SpringBoot注解参数校验,给代码穿上“防弹衣”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot注解参数校验,给代码穿上“防弹衣”

大家好,我是小悟。

一、参数校验:程序员的“防杠精神器”

假如你的API像个热情的饭店服务员,用户说“随便来点吃的”,你就真给他上了盘空气——这可不妙!参数校验就像是那个会耐心问“要辣的还是不辣的?要牛肉还是鸡肉?”的细心服务员,确保不闹出“我要咖啡你却给我上了杯洗脚水”的尴尬。

SpringBoot的注解校验就像给你的方法参数请了个私人保镖,专门拦截那些不靠谱的输入。没有它?用户传个null过来,你的程序可能就会表演“当场崩溃”的绝活。

二、详细步骤:给代码戴上“紧箍咒”

第1步:先来点“开胃菜”——添加依赖

<!-- pom.xml里加入这个,就像泡面加卤蛋,标配! --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>

第2步:创建个“相亲简历”DTO类

import javax.validation.constraints.*; import java.util.Date; import java.util.List; /** * 用户注册DTO - 比相亲网站的个人资料要求还严格 */ public class UserRegisterDTO { @NotBlank(message = "用户名不能为空,难道您是无名氏?") @Size(min = 2, max = 20, message = "用户名长度在2-20之间,太短没存在感,太长记不住") private String username; @Email(message = "邮箱格式不对,这可不是在写情书,随便写写就行") private String email; @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$", message = "密码至少8位,包含字母和数字,别再用123456了!") private String password; @Min(value = 18, message = "未满18岁?小朋友先去写作业") @Max(value = 120, message = "超过120岁?您是老神仙吧") private Integer age; @NotNull(message = "手机号必须填,不然外卖到了找谁?") private String phone; @AssertTrue(message = "必须接受协议,虽然可能没人看") private Boolean acceptedAgreement; @Future(message = "预约时间必须是未来,时光机还没发明呢") private Date appointmentTime; @Size(min = 1, max = 3, message = "最多选3个爱好,您是想成为全能超人吗?") private List<String> hobbies; // 此处省略getter和setter,但它们确实存在,我发誓! // 用Lombok的@Data也行,但今天咱们保持纯洁的Java关系 // 自定义校验注解示例 @ValidGender private String gender; }

第3步:自定义校验注解——打造专属“安检仪”

import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; /** * 性别校验注解 - 咱们思想很开放,但数据要规范 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = GenderValidator.class) public @interface ValidGender { String message() default "性别必须是男、女或保密,您这是来自火星吗?"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } /** * 性别校验器 - 严肃的判官 */ public class GenderValidator implements ConstraintValidator<ValidGender, String> { private static final Set<String> VALID_GENDERS = new HashSet<>(Arrays.asList("男", "女", "保密")); @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) { return true; // 用@NotNull管非空,咱们只管格式 } return VALID_GENDERS.contains(value); } }

第4步:控制器里使用——给API装上“安检门”

import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotBlank; @RestController @RequestMapping("/api/users") @Validated // 这个注解让方法参数校验生效,就像给方法吃了“严格丸” public class UserController { /** * 注册用户 - 参数校验比丈母娘挑女婿还严格 */ @PostMapping("/register") public Result register(@RequestBody @Valid UserRegisterDTO userDTO) { // 如果参数校验失败,根本走不到这里 // 就像考试不及格,进不了下一轮面试 return Result.success("注册成功,恭喜通过严格审查!"); } /** * 方法参数校验 - 连路径变量都不放过 */ @GetMapping("/{id}") public Result getUser( @PathVariable @Min(value = 1, message = "ID必须大于0,您这是要找空气用户吗?") Long id, @RequestParam @NotBlank(message = "令牌不能为空,您这是想蒙混过关?") String token) { return Result.success("找到了用户ID: " + id); } /** * 分组校验 - 根据不同场景使用不同规则 * 就像上班穿正装,在家穿睡衣,场合要分清 */ @PostMapping("/update") public Result updateUser(@RequestBody @Validated(UserUpdateGroup.class) UserUpdateDTO dto) { return Result.success("更新成功"); } } // 分组接口定义 interface UserUpdateGroup {} interface UserCreateGroup {}

第5步:全局异常处理——优雅的“救火队员”

import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.validation.FieldError; /** * 全局异常处理器 - 专业收拾校验失败的烂摊子 */ @RestControllerAdvice public class GlobalExceptionHandler { /** * 处理参数校验异常 - 把技术语言翻译成人话 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Result handleValidationException(MethodArgumentNotValidException ex) { // 收集所有错误信息,就像收集考试错题 Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach(error -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); return Result.error(400, "参数校验失败", errors) .setMessage("您提交的数据有点小问题,请检查后再试哦~"); } /** * 处理ConstraintViolationException - 方法参数校验失败 */ @ExceptionHandler(ConstraintViolationException.class) public Result handleConstraintViolationException(ConstraintViolationException ex) { List<String> errors = ex.getConstraintViolations().stream() .map(violation -> violation.getMessage()) .collect(Collectors.toList()); return Result.error(400, "参数不合法", errors); } } /** * 统一返回结果 - 给前端一个标准的“成绩单” */ @Data @AllArgsConstructor @NoArgsConstructor public class Result<T> { private Integer code; private String message; private T data; private Long timestamp = System.currentTimeMillis(); public static <T> Result<T> success(T data) { return new Result<>(200, "成功", data); } public static <T> Result<T> error(Integer code, String message, T data) { return new Result<>(code, message, data); } public Result<T> setMessage(String message) { this.message = message; return this; } }

第6步:进阶玩法——嵌套校验和集合校验

/** * 订单DTO - 俄罗斯套娃式的校验 */ public class OrderDTO { @NotNull(message = "订单信息不能为空") @Valid // 这个注解让嵌套校验生效,就像班主任检查每个学生的作业 private UserDTO user; @Valid // 集合也要逐个校验,一个都别想逃 private List<@Valid OrderItemDTO> items; @Valid private AddressDTO address; } /** * 地址DTO - 精确到门牌号 */ public class AddressDTO { @NotBlank(message = "省份不能空,您这是要寄到外太空?") private String province; @NotBlank(message = "城市不能空") private String city; @Size(min = 5, max = 100, message = "详细地址5-100字,说清楚点,快递员会感谢您") private String detail; }

三、测试一下:看看“保镖”工作认不认真

// 测试Controller - 专门捣乱看系统反应 @SpringBootTest @AutoConfigureMockMvc class UserControllerTest { @Autowired private MockMvc mockMvc; @Test void testRegisterWithInvalidData() throws Exception { String invalidUserJson = """ { "username": "A", // 太短了! "email": "not-an-email", // 这不是邮箱 "password": "123", // 太弱了 "age": 10, // 未成年! "phone": null, // 空值 "acceptedAgreement": false, // 不同意协议 "appointmentTime": "2020-01-01", // 过去的时间 "hobbies": ["吃饭", "睡觉", "打豆豆", "刷手机", "发呆"] // 爱好太多 } """; mockMvc.perform(MockMvcRequestBuilders.post("/api/users/register") .contentType(MediaType.APPLICATION_JSON) .content(invalidUserJson)) .andExpect(status().isBadRequest()) // 应该返回400 .andExpect(jsonPath("$.code").value(400)) .andExpect(jsonPath("$.data").exists()) // 错误详情 .andDo(print()); // 打印响应,看看“保镖”怎么怼你 } }

四、性能优化小贴士

/** * 校验配置 - 让校验既严格又高效 */ @Configuration public class ValidationConfig { @Bean public Validator validator() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); // 可以在这里配置一些自定义设置 // 比如缓存校验器,避免重复创建 return validator; } /** * 快速失败模式 - 发现一个错误就立即返回 * 就像考试发现第一题错了就交卷(不建议真人尝试) */ @Bean public Validator fastFailValidator() { return Validation.byDefaultProvider() .configure() .addProperty("hibernate.validator.fail_fast", "true") .buildValidatorFactory() .getValidator(); } }

总结:参数校验的“人生哲理”

  1. 为什么需要参数校验?
    • 防止GIGO(垃圾进,垃圾出)——输入决定输出质量
    • 安全第一:很多安全漏洞都源于不可信的输入
    • 用户体验:早发现错误,早提示用户,别让用户猜谜
  2. 注解校验的优点:
    • 声明式:像贴标签一样简单,告别一堆if-else
    • 集中管理:规则在实体类上一目了然
    • 易于维护:改注解就能改规则,不用翻业务代码
    • 丰富内置:Spring提供了几十种注解,总有一款适合你
  3. 最佳实践建议:
    • 在DTO层做校验,保持业务层纯洁
    • 错误消息要友好,说人话,别甩技术术语
    • 区分必填和非必填字段,别要求用户填宇宙
    • 复杂逻辑用自定义校验器,别硬塞到一个注解里
    • 记得处理异常,给前端统一的错误格式
  4. 总结:
    参数校验就像给你的代码请了个:
    • 门卫大爷:不合格的一律不让进
    • 语文老师:检查格式对不对,内容全不全
    • 健身教练:严格要求,不容马虎
    • 相声演员:出错时还能用幽默的方式告诉你

严谨的程序员对待输入就像猫奴对待猫主子,既要有爱,也要有规矩!你的API会因为良好的参数校验而变得更加健壮、安全、用户友好。

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 14:55:21

2026必备!自考论文神器TOP9:AI论文工具深度测评与推荐

2026必备&#xff01;自考论文神器TOP9&#xff1a;AI论文工具深度测评与推荐 2026年自考论文写作新趋势与工具测评逻辑 随着人工智能技术的不断进步&#xff0c;AI论文工具已成为自考学生提升写作效率、优化论文质量的重要助手。然而&#xff0c;面对市场上琳琅满目的产品&…

作者头像 李华
网站建设 2026/4/2 3:00:13

vue2和vue3的区别

响应式系统实现方式Vue2 使用 Object.defineProperty 实现数据劫持&#xff0c;需递归遍历对象属性&#xff0c;无法检测新增/删除属性&#xff0c;需通过 Vue.set 或 Vue.delete 处理。 Vue3 改用 Proxy 代理整个对象&#xff0c;支持动态新增/删除属性&#xff0c;性能更高且…

作者头像 李华
网站建设 2026/4/2 23:15:03

西门子罗宾康变频器底板A1A098194

西门子罗宾康变频器底板 A1A098194 技术详解在工业自动化驱动系统中&#xff0c;变频器扮演着至关重要的角色&#xff0c;负责精确控制电动机的速度、转矩和方向。西门子罗宾康&#xff08;Robicon&#xff09;系列变频器以其高性能和可靠性在业界享有盛誉。作为变频器内部结构…

作者头像 李华
网站建设 2026/4/1 23:14:45

IQuest-Coder-V1:基于代码流训练的编程逻辑增强模型;Human Face Emotions:基于多标注维度的人脸情绪识别数据集

当前&#xff0c;AI 代码生成工具虽然普及&#xff0c;但常面临生成代码逻辑僵化、上下文理解不足、难以模仿真实开发流程的挑战。许多模型仅学习代码片段的「静态快照」&#xff0c;缺乏对代码为何及如何修改的深层理解&#xff0c;导致生成的代码实用性受限。基于此&#xff…

作者头像 李华
网站建设 2026/3/30 16:49:07

2026医护学习垂直平台推荐:聚焦题库平台

2026年医护类考试迎来考纲大调整&#xff0c;护资案例题占比飙升至45%-80%&#xff0c;执医新增大量临床实操考点&#xff0c;职称考试对知识应用能力要求更高。刷题作为备考核心环节&#xff0c;选对题库平台能让效率翻倍。本文基于考纲适配性、题库质量、解析专业度、智能功能…

作者头像 李华
网站建设 2026/3/30 22:59:03

你的SCI论文总被拒?中国学生最易踩的6个坑

在科研领域&#xff0c;发表SCI论文是众多中国学生追求的目标&#xff0c;它代表着学术水平和科研能力得到国际认可。然而&#xff0c;不少学生在投稿过程中屡屡受挫&#xff0c;论文被拒的情况时有发生。下面就为大家详细剖析中国学生最容易踩到的6个“坑”。 坑一&#xff1a…

作者头像 李华