1. bean validation基础注解
spring validation集成了jsr-380 (bean validation 2.0)规范,提供了一系列开箱即用的校验注解。
常用注解示例
@data public class userdto { @notnull(message = "用户id不能为空") private long id; @notblank(message = "用户名不能为空") @size(min = 4, max = 20, message = "用户名长度必须在4到20个字符之间") private string username; @email(message = "邮箱格式不正确") private string email; @min(value = 18, message = "年龄必须大于或等于18") @max(value = 120, message = "年龄必须小于或等于120") private integer age; @past(message = "出生日期必须是过去的日期") private localdate birthdate; @pattern(regexp = "^1[3-9]\d{9}$", message = "手机号码格式不正确") private string phonenumber; }
在控制器中应用
@restcontroller @requestmapping("/api/users") public class usercontroller { @postmapping public responseentity<userdto> createuser(@requestbody @valid userdto userdto, bindingresult bindingresult) { if (bindingresult.haserrors()) { // 处理验证错误 throw new validationexception(bindingresult); } // 处理业务逻辑 return responseentity.ok(userdto); } }
最佳实践:使用有意义的错误消息,保持一致的命名风格,避免在实体类上直接使用验证注解,而是在dto对象上应用验证规则。
2. 自定义约束验证器
spring validation允许开发者创建自定义约束,满足特定业务规则的验证需求。
定义自定义约束注解
@target({elementtype.field, elementtype.parameter}) @retention(retentionpolicy.runtime) @constraint(validatedby = uniqueusernamevalidator.class) public @interface uniqueusername { string message() default "用户名已存在"; class<?>[] groups() default {}; class<? extends payload>[] payload() default {}; }
实现验证器
public class uniqueusernamevalidator implements constraintvalidator<uniqueusername, string> { @autowired private userrepository userrepository; @override public boolean isvalid(string username, constraintvalidatorcontext context) { if (username == null) { return true; // 让@notnull处理空值 } return !userrepository.existsbyusername(username); } }
应用自定义约束
public class userregistrationdto { @notblank @size(min = 4, max = 20) @uniqueusername private string username; // 其他字段... }
使用场景:验证业务特定规则,如唯一性约束、密码复杂度、信用卡格式等。
3. 分组验证
分组验证允许根据不同场景应用不同的验证规则,例如创建和更新操作可能需要不同的验证逻辑。
定义验证分组
// 定义验证分组接口 public interface validationgroups { interface create {} interface update {} }
应用分组到约束
@data public class productdto { @null(groups = validationgroups.create.class, message = "创建产品时id必须为空") @notnull(groups = validationgroups.update.class, message = "更新产品时id不能为空") private long id; @notblank(groups = {validationgroups.create.class, validationgroups.update.class}) private string name; @positiveorzero(groups = validationgroups.create.class) @positive(groups = validationgroups.update.class) private bigdecimal price; }
在控制器中指定分组
@restcontroller @requestmapping("/api/products") public class productcontroller { @postmapping public responseentity<productdto> createproduct( @requestbody @validated(validationgroups.create.class) productdto productdto) { // 创建产品逻辑 return responseentity.ok(productdto); } @putmapping("/{id}") public responseentity<productdto> updateproduct( @pathvariable long id, @requestbody @validated(validationgroups.update.class) productdto productdto) { // 更新产品逻辑 return responseentity.ok(productdto); } }
提示:注意使用@validated
注解而不是@valid
,因为只有前者支持分组验证。
4. 嵌套验证
嵌套验证允许验证复杂对象结构中的嵌套对象。
定义嵌套对象
@data public class orderdto { @notnull private long id; @notnull @valid // 标记需要级联验证的字段 private customerdto customer; @notempty @valid // 验证集合中的每个元素 private list<orderitemdto> items; } @data public class customerdto { @notnull private long id; @notblank private string name; @email private string email; @valid // 进一步嵌套验证 private addressdto address; }
关键点:在需要级联验证的字段上添加@valid
注解,确保验证深入到嵌套对象中。
5. 方法级别验证
spring validation不仅可以用于控制器参数,还可以应用于服务层的方法。
启用方法级别验证
@configuration @enablemethodvalidation public class validationconfig { // 配置内容 }
定义带验证的服务方法
@service public class userservice { @validated public user createuser(@valid userdto userdto) { // 业务逻辑 return new user(); } @notnull public user findbyid(@min(1) long id) { // 查询逻辑 return new user(); } @validated(validationgroups.update.class) public void updateuser(@valid userdto userdto) { // 更新逻辑 } }
应用场景:确保服务层方法接收到的参数和返回的结果符合预期,增强代码的健壮性。
6. 错误消息处理和国际化
spring validation提供了强大的错误消息处理和国际化支持。
自定义错误消息
在validationmessages.properties
文件中定义:
# validationmessages.properties javax.validation.constraints.notempty.message=字段不能为空 javax.validation.constraints.email.message=不是有效的电子邮箱地址 user.name.size=用户名长度必须在{min}到{max}个字符之间
国际化错误消息
创建特定语言的属性文件:
# validationmessages_en.properties javax.validation.constraints.notempty.message=field cannot be empty javax.validation.constraints.email.message=not a valid email address user.name.size=username must be between {min} and {max} characters # validationmessages_zh_cn.properties javax.validation.constraints.notempty.message=字段不能为空 javax.validation.constraints.email.message=不是有效的电子邮箱地址 user.name.size=用户名长度必须在{min}到{max}个字符之间
使用自定义消息
@size(min = 4, max = 20, message = "{user.name.size}") private string username;
7. 程序化验证
除了注解驱动的验证,spring validation还支持以编程方式进行验证。
使用validator手动验证对象
@service public class validationservice { private final validator validator; public validationservice(validator validator) { this.validator = validator; } public <t> void validate(t object) { set<constraintviolation<t>> violations = validator.validate(object); if (!violations.isempty()) { throw new constraintviolationexception(violations); } } public <t> void validatewithgroup(t object, class<?>... groups) { set<constraintviolation<t>> violations = validator.validate(object, groups); if (!violations.isempty()) { throw new constraintviolationexception(violations); } } public <t> list<string> getvalidationerrors(t object) { return validator.validate(object).stream() .map(constraintviolation::getmessage) .collect(collectors.tolist()); } }
使用场景:在复杂业务逻辑中需要条件性验证,或者验证非控制器传入的对象时。
8. 组合约束
组合约束允许将多个基本约束组合成一个更复杂的约束,减少代码重复。
创建组合约束
@notnull @size(min = 8, max = 30) @pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[a-z])(?=.*[@#$%^&+=]).*$", message = "密码必须包含至少一个数字、小写字母、大写字母和特殊字符") @target({elementtype.field, elementtype.parameter}) @retention(retentionpolicy.runtime) @constraint(validatedby = {}) public @interface strongpassword { string message() default "密码不符合安全要求"; class<?>[] groups() default {}; class<? extends payload>[] payload() default {}; }
应用组合约束
public class passwordchangedto { @notblank private string oldpassword; @strongpassword private string newpassword; @notblank private string confirmpassword; }
优点:提高代码可读性和可维护性,确保验证规则在整个应用中保持一致。
9. 跨字段验证
跨字段验证允许根据多个字段之间的关系进行验证。
创建类级别约束
@target({elementtype.type}) @retention(retentionpolicy.runtime) @constraint(validatedby = passwordmatchesvalidator.class) public @interface passwordmatches { string message() default "确认密码与新密码不匹配"; class<?>[] groups() default {}; class<? extends payload>[] payload() default {}; string field(); string fieldmatch(); }
实现验证器
public class passwordmatchesvalidator implements constraintvalidator<passwordmatches, object> { private string field; private string fieldmatch; @override public void initialize(passwordmatches constraintannotation) { this.field = constraintannotation.field(); this.fieldmatch = constraintannotation.fieldmatch(); } @override public boolean isvalid(object value, constraintvalidatorcontext context) { try { object fieldvalue = beanutils.getpropertydescriptor(value.getclass(), field) .getreadmethod().invoke(value); object fieldmatchvalue = beanutils.getpropertydescriptor(value.getclass(), fieldmatch) .getreadmethod().invoke(value); return (fieldvalue != null) && fieldvalue.equals(fieldmatchvalue); } catch (exception e) { return false; } } }
应用类级别约束
@data @passwordmatches(field = "newpassword", fieldmatch = "confirmpassword") public class passwordchangedto { @notblank private string oldpassword; @strongpassword private string newpassword; @notblank private string confirmpassword; }
使用场景:验证密码确认、日期范围比较、最小/最大值比较等。
总结
spring validation提供了一套全面而强大的数据校验工具,从基本的注解验证到复杂的自定义约束,从单一字段验证到跨字段关系验证,都有相应的解决方案。
合理利用这些验证工具,不仅能提高应用的健壮性和安全性,还能改善代码质量和可维护性。
以上就是spring validation中9个数据校验工具使用指南的详细内容,更多关于spring validation数据校验工具的资料请关注代码网其它相关文章!
发表评论