自定义异常不用于配合校验注解自动触发,而是由全局异常处理器捕获MethodArgumentNotValidException等标准异常后,封装为统一响应;仅在JSR无法覆盖的业务规则校验时才主动throw,且ConstraintValidator中不可手动抛异常。

自定义异常不是用来“配合”校验注解自动触发的,而是用来统一捕获校验失败后抛出的标准异常,并转换成你想要的响应格式。
校验注解本身不抛出自定义异常
使用 @NotNull、@NotBlank 等注解时,Spring 会委托 Hibernate Validator 执行校验。校验失败时,默认抛出的是 MethodArgumentNotValidException(针对 @RequestBody + @Valid)或 ConstraintViolationException(针对 @Valid 在普通对象上)。这些是框架自带的异常类型,不是你写的自定义类。
用全局异常处理器拦截并转成自定义异常响应
你真正需要做的,是在 Controller 层之外,写一个全局异常处理器,捕获上述标准异常,再封装成带业务码、提示语、错误字段的 JSON 响应——这个响应结构,往往由你的自定义异常类(比如 ValidationException 或 BusinessException)所定义,但注意:它通常不被直接 throw,而是作为响应体的数据模型。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 定义统一响应结果类(如
RespData),含 code、msg、data 字段 - 编写
@ControllerAdvice类,用@ExceptionHandler(MethodArgumentNotValidException.class)拦截 - 从
BindingResult或异常中提取字段错误,组装成 map 或 list - 返回
RespData.error(400, "参数校验失败").put("errors", errors)
自定义异常类可作为业务校验的补充,而非替代 JSR 校验
当 JSR 注解无法覆盖逻辑时(例如“密码和确认密码必须一致”“新增时手机号不能已存在”),你才需要在 Service 或 Controller 中手动校验,并主动 throw 自定义异常(如 BusinessException)。这类异常需单独被全局处理器捕获,与校验异常分开处理。
立即学习“Java免费学习笔记(深入)”;
- 例如:
if (!user.getPassword().equals(user.getConfirmPassword())) { throw new BusinessException("两次输入的密码不一致"); } - 此时全局处理器中增加
@ExceptionHandler(BusinessException.class)分支 - 保持两类异常路径清晰:JSR 负责基础格式,自定义异常负责业务规则
不要在自定义校验注解的 validate 方法里 throw 自定义异常
实现 ConstraintValidator 接口时,isValid() 方法只返回 true/false。校验失败的提示信息靠注解上的 message 属性或资源文件控制,由框架统一收集并包装进 FieldError。手动 throw 异常会导致校验流程中断,BindingResult 拿不到完整错误列表,破坏批量校验能力。

















