当前位置: 代码网 > it编程>编程语言>Java > SpringBoot使用Validation实现接口校验的超全使用指南

SpringBoot使用Validation实现接口校验的超全使用指南

2026年03月01日 Java 我要评论
一、依赖引入spring boot 提供的spring-boot-starter-validation依赖整合了 jsr-380 规范(bean validation 2.0)及 hibernate

一、依赖引入

spring boot 提供的 spring-boot-starter-validation 依赖整合了 jsr-380 规范(bean validation 2.0)及 hibernate validator 实现,支持便捷的请求参数校验功能,无需手动编写重复的校验逻辑。

在项目 pom.xml 中引入依赖(spring boot 2.3+ 需显式引入,2.3 以下版本可通过 spring-boot-starter-web 间接依赖):

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-validation</artifactid>
    <!-- 无需指定版本,spring boot 父工程已统一管理 -->
</dependency>

二、核心使用步骤(对象参数校验)

spring mvc 中最常用的校验场景是对象类型的请求参数校验,需遵循「注解标记→对象定义→异常处理」三步法:

步骤 1:controller 方法标记 @valid

在接收的对象参数前添加 @valid 注解(或 @validated),告知 spring mvc 对该参数执行校验:

import org.springframework.validation.annotation.validated;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;

import javax.validation.valid;
import javax.servlet.http.httpservletrequest;

@restcontroller
@requestmapping("/test")
// 类上添加 @validated 可支持方法参数(非对象)的校验
@validated
public class testcontroller {

    /**
     * 测试对象参数校验
     * @param req 待校验的请求对象
     * @param request 请求上下文
     * @return 响应结果
     */
    @requestmapping(value = "/req.json")
    public object test(@valid testrequest req, httpservletrequest request) {
        // 校验通过后才会执行此处业务逻辑
        return "请求成功,req=" + req.getreq();
    }

    /**
     * 扩展:基本类型参数校验(需类上添加 @validated)
     * 校验不通过会抛出 constraintviolationexception
     */
    @requestmapping(value = "/base.json")
    public object testbaseparam(
            @notblank(message = "用户名不能为空") string username,
            @min(value = 18, message = "年龄不能小于18岁") integer age) {
        return "用户名:" + username + ",年龄:" + age;
    }
}

步骤 2:定义请求对象并添加校验注解

创建请求参数对应的实体类,在需要校验的字段上添加「常用校验注解」,并指定错误提示信息:

import javax.validation.constraints.notblank;

public class testrequest {

    // @notblank:字符串不能为 null 且去除首尾空格后长度 > 0
    @notblank(message = "req参数不能为空(不能是空白字符)")
    private string req;

    // 必须提供 getter/setter,否则 spring mvc 无法注入参数
    public string getreq() {
        return req;
    }

    public void setreq(string req) {
        this.req = req;
    }
}

步骤 3:全局异常处理器统一处理校验异常

校验不通过时,spring mvc 会抛出不同类型的异常(对象参数抛出 bindexception/methodargumentnotvalidexception,基本类型参数抛出 constraintviolationexception),需通过「全局异常处理器」捕获并统一返回格式化响应:

import org.springframework.context.messagesource;
import org.springframework.context.i18n.localecontextholder;
import org.springframework.validation.objecterror;
import org.springframework.web.bind.methodargumentnotvalidexception;
import org.springframework.web.bind.annotation.exceptionhandler;
import org.springframework.web.bind.annotation.restcontrolleradvice;

import javax.annotation.resource;
import javax.validation.constraintviolation;
import javax.validation.constraintviolationexception;
import java.util.locale;
import java.util.set;

/**
 * 全局异常处理器:统一处理参数校验异常
 */
@restcontrolleradvice
public class globalvalidationexceptionhandler {

    // 用于国际化消息解析(可选)
    @resource
    private messagesource messagesource;

    /**
     * 处理对象参数校验异常(@valid 标记的对象)
     * 包括:bindexception(表单提交)、methodargumentnotvalidexception(json 提交)
     */
    @exceptionhandler({bindexception.class, methodargumentnotvalidexception.class})
    public result<?> handleobjectvalidationexception(exception ex) {
        objecterror objecterror = null;
        // 区分不同的对象校验异常类型
        if (ex instanceof bindexception) {
            // 表单提交(application/x-www-form-urlencoded)
            objecterror = ((bindexception) ex).getbindingresult().getallerrors().get(0);
        } else if (ex instanceof methodargumentnotvalidexception) {
            // json 提交(application/json)
            objecterror = ((methodargumentnotvalidexception) ex).getbindingresult().getallerrors().get(0);
        }

        // 解析错误信息(支持国际化)
        locale locale = localecontextholder.getlocale();
        string errormsg = messagesource.getmessage(objecterror, locale);
        return result.error(400, "参数校验失败", errormsg);
    }

    /**
     * 处理基本类型/单个参数校验异常(@validated 标记的类)
     */
    @exceptionhandler(constraintviolationexception.class)
    public result<?> handlebaseparamvalidationexception(constraintviolationexception ex) {
        set<constraintviolation<?>> violations = ex.getconstraintviolations();
        // 获取第一个错误信息(也可收集所有错误)
        string errormsg = violations.iterator().next().getmessage();
        return result.error(400, "参数校验失败", errormsg);
    }

    // 通用响应类(简化示例)
    static class result<t> {
        private int code;
        private string msg;
        private t data;

        public static <t> result<t> error(int code, string msg, t data) {
            result<t> result = new result<>();
            result.code = code;
            result.msg = msg;
            result.data = data;
            return result;
        }

        public static <t> result<t> success(t data) {
            result<t> result = new result<>();
            result.code = 200;
            result.msg = "操作成功";
            result.data = data;
            return result;
        }

        // getter/setter 省略
    }
}

三、常用校验注解详解

jsr-380 规范定义了一系列标准校验注解,hibernate validator 扩展了部分注解,以下是开发中最常用的注解及使用场景:

注解名称核心作用适用类型关键属性说明
@notblank字符串不能为 null 且去除首尾空格后长度 > 0stringmessage:错误提示
@notnull值不能为 null(不校验空字符串、空集合)所有类型(对象、基本类型包装类)-
@notempty集合 / 数组 / 字符串不能为 null 且长度 > 0(字符串不忽略首尾空格)string、collection、map、数组-
@min(value)数字不能小于 value(不支持 float/double,避免精度问题)数值类型(integer、long 等)value:最小值;inclusive:是否包含最小值(默认 true)
@max(value)数字不能大于 value(不支持 float/double)数值类型同 @min
@decimalmin(value)支持小数的最小值校验(可指定数值格式)数值类型、stringvalue:最小值(支持小数);inclusive:是否包含最小值
@decimalmax(value)支持小数的最大值校验数值类型、string同 @decimalmin
@email字符串必须符合邮箱格式(支持自定义正则)stringregexp:自定义邮箱正则;flags:正则匹配模式
@pattern(regexp)字符串必须匹配指定正则表达式stringregexp:正则表达式;flags:匹配模式(如 case_insensitive 忽略大小写)
@size(min, max)集合 / 数组 / 字符串的长度在 [min, max] 范围内string、collection、map、数组min:最小长度;max:最大长度(默认 integer.max_value)
@future日期必须是当前时间之后的时间date、localdatetime 等-
@futureorpresent日期必须是当前时间或之后的时间日期类型-
@past日期必须是当前时间之前的时间日期类型-
@pastorpresent日期必须是当前时间或之前的时间日期类型-
@positive数字必须是正数(> 0)数值类型-
@positiveorzero数字必须是正数或 0(≥ 0)数值类型-
@negative数字必须是负数(< 0)数值类型-
@negativeorzero数字必须是负数或 0(≤ 0)数值类型-
@digits(integer, fraction)数字的整数部分位数 ≤ integer,小数部分位数 ≤ fraction数值类型、stringinteger:整数最大位数;fraction:小数最大位数

注解使用示例

import javax.validation.constraints.*;
import java.time.localdatetime;

public class userrequest {
    @notblank(message = "用户名不能为空")
    @size(min = 2, max = 20, message = "用户名长度必须在2-20个字符之间")
    private string username;

    @notnull(message = "年龄不能为空")
    @min(value = 18, message = "年龄不能小于18岁")
    @max(value = 60, message = "年龄不能大于60岁")
    private integer age;

    @email(message = "邮箱格式不正确", regexp = "^[a-za-z0-9_-]+@[a-za-z0-9_-]+(\\.[a-za-z0-9_-]+)+$")
    private string email;

    @past(message = "生日必须是过去的时间")
    private localdatetime birthday;

    @pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private string phone;

    @decimalmin(value = "0.01", message = "金额不能小于0.01")
    @decimalmax(value = "10000.00", message = "金额不能大于10000.00")
    private double amount;

    // getter/setter 省略
}

四、自定义校验注解(扩展能力)

当默认校验注解无法满足业务需求时(如「手机号格式校验」「自定义状态值校验」),可通过 jsr-380 规范提供的扩展机制实现自定义校验注解。

实现步骤(以「手机号校验」为例)

步骤 1:创建自定义校验注解

注解必须标注 @constraint 并指定对应的验证器,同时包含 message、groups、payload 三个必填属性(jsr-380 规范要求):

import javax.validation.constraint;
import javax.validation.payload;
import java.lang.annotation.*;

/**
 * 自定义手机号校验注解
 */
@target({elementtype.field, elementtype.parameter}) // 支持字段和方法参数
@retention(retentionpolicy.runtime) // 运行时生效
@documented
@constraint(validatedby = phonevalidator.class) // 关联自定义验证器
public @interface phone {

    // 错误提示信息(支持国际化,默认值可引用配置文件)
    string message() default "手机号格式不正确(必须是11位有效手机号)";

    // 校验分组(用于多场景校验,如新增/编辑不同规则)
    class<?>[] groups() default {};

    // 负载信息(用于传递额外校验元数据)
    class<? extends payload>[] payload() default {};
}

步骤 2:实现 constraintvalidator 接口

创建验证器类,实现 constraintvalidator<a, t> 接口(a 为自定义注解,t 为校验目标类型),核心逻辑在 isvalid 方法中:

import javax.validation.constraintvalidator;
import javax.validation.constraintvalidatorcontext;
import java.util.regex.pattern;

/**
 * 手机号校验器:实现 constraintvalidator 接口
 */
public class phonevalidator implements constraintvalidator<phone, string> {

    // 手机号正则表达式(支持13-9开头的11位数字)
    private static final pattern phone_pattern = pattern.compile("^1[3-9]\\d{9}$");

    /**
     * 初始化方法:可获取注解的属性值(如自定义正则)
     */
    @override
    public void initialize(phone constraintannotation) {
        // 若注解有自定义属性(如 regexp),可在此处获取并初始化
        constraintvalidator.super.initialize(constraintannotation);
    }

    /**
     * 校验核心方法:返回 true 表示校验通过,false 表示失败
     * @param value 待校验的值(手机号字符串)
     * @param context 校验上下文(可用于自定义错误信息)
     */
    @override
    public boolean isvalid(string value, constraintvalidatorcontext context) {
        // 1. 允许值为 null(若不允许 null,需配合 @notnull 注解)
        if (value == null) {
            return true;
        }
        // 2. 正则匹配校验
        return phone_pattern.matcher(value).matches();
    }
}

步骤 3:使用自定义注解

与默认注解用法完全一致,可直接标注在字段或参数上:

public class userrequest {
    @phone(message = "手机号格式错误,请输入11位有效手机号")
    private string phone;

    // 配合 @notnull 注解,禁止手机号为 null
    @notnull(message = "手机号不能为空")
    @phone
    private string requiredphone;

    // getter/setter 省略
}

五、进阶使用技巧

1. 分组校验

当同一个对象在不同场景(如「新增用户」和「编辑用户」)有不同校验规则时,可通过「分组校验」实现:

// 1. 定义分组接口(无需实现)
public interface addgroup {}
public interface updategroup {}

// 2. 注解指定分组
public class userrequest {
    @notnull(groups = addgroup.class, message = "新增时id不能为空")
    @null(groups = updategroup.class, message = "编辑时id必须为空")
    private long id;

    @notblank(groups = {addgroup.class, updategroup.class}, message = "用户名不能为空")
    private string username;
}

// 3. controller 指定分组校验
@restcontroller
@requestmapping("/user")
public class usercontroller {
    // 新增用户:只校验 addgroup 分组的注解
    @postmapping("/add")
    public result<?> add(@validated(addgroup.class) userrequest request) {
        return result.success("新增成功");
    }

    // 编辑用户:只校验 updategroup 分组的注解
    @postmapping("/update")
    public result<?> update(@validated(updategroup.class) userrequest request) {
        return result.success("编辑成功");
    }
}

2. 嵌套校验

当对象中包含另一个对象属性,且需要对嵌套对象进行校验时,需在嵌套对象字段上添加 @valid 注解:

public class userrequest {
    @notblank(message = "用户名不能为空")
    private string username;

    // 嵌套对象校验:必须添加 @valid 注解
    @valid
    @notnull(message = "地址信息不能为空")
    private addressrequest address;

    // 嵌套对象类
    public static class addressrequest {
        @notblank(message = "省份不能为空")
        private string province;

        @notblank(message = "城市不能为空")
        private string city;

        // getter/setter 省略
    }

    // getter/setter 省略
}

3. 国际化错误提示

将错误提示信息存入国际化配置文件,通过 messagesource 解析:

# src/main/resources/messages.properties(默认)
user.username.notblank=用户名不能为空
user.phone.invalid=手机号格式不正确

# src/main/resources/messages_zh_cn.properties(中文)
user.username.notblank=用户名不能为空
user.phone.invalid=手机号格式不正确

# src/main/resources/messages_en_us.properties(英文)
user.username.notblank=username cannot be blank
user.phone.invalid=phone number format is invalid

使用时引用配置文件中的 key:

public class userrequest {
    @notblank(message = "{user.username.notblank}")
    private string username;

    @phone(message = "{user.phone.invalid}")
    private string phone;
}

六、常见问题与注意事项

1. @valid 与 @validated 的区别:

  • @valid:jsr-380 标准注解,支持对象校验、嵌套校验,不支持分组校验和方法参数(非对象)校验。
  • @validated:spring 扩展注解,支持分组校验、方法参数(非对象)校验,不支持嵌套校验(需配合 @valid)。

2. 基本类型参数校验失败:

  • 需在 controller 类上添加 @validated 注解,否则 methodvalidationpostprocessor 无法拦截方法。
  • 校验失败会抛出 constraintviolationexception,需在全局异常处理器中单独处理。

3. json 提交与表单提交的异常差异:

  • json 提交(content-type: application/json):校验失败抛出 methodargumentnotvalidexception。
  • 表单提交(content-type: application/x-www-form-urlencoded):校验失败抛出 bindexception。

4. float/double 类型的数值校验:

  • 避免使用 @min/@max,因浮点型精度问题可能导致校验失效,建议使用 @decimalmin/@decimalmax 或转换为 string 类型后用 @pattern 校验。

5. 自定义注解不生效:

  • 确保注解标注了 @constraint 并指定了 validatedby 属性。
  • 验证器类必须实现 constraintvalidator 接口,且泛型与注解、目标类型一致。
  • 注解的 retention 必须为 runtime(运行时才能被反射获取)。

以上就是springboot使用validation实现接口校验的超全使用指南的详细内容,更多关于springboot validation接口校验的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com