作者:金城\来源:juejin.im/post/5d3fbeb46fb9a06b317b3c48遇到大量的很痛苦验证参数。在业务中,抛出异常或不断返回异常时的验证信息在代码中是相当冗长的,充满了if-else等验证码。今天我们就来学习spring的javax.validation注解参数校验。1、为什么要使用validatorjavax.validation的一系列注解来帮助我们完成参数校验,避免繁琐的串行校验。否则,我们的代码如下所示://http://localhost:8080/api/user/save/serial/***Takeserialverification**@paramuserVO*@return*/@PostMapping("/save/serial")publicObjectsave(@RequestBodyUserVOuserVO){Stringmobile=userVO.getMobile();//手动一个一个验证参数~写法if(StringUtils.isBlank(mobile)){returnRspDTO.paramFail("mobile:手机号不能为空");}elseif(!Pattern.matches("^[1][3,4,5,6,7,8,9][0-9]{9}$",mobile)){returnRspDTO.paramFail("mobile:手机号码格式错误");}//throw自定义异常等~写法if(StringUtils.isBlank(userVO.getUsername())){thrownewBizException(Constant.PARAM_FAIL_CODE,"用户名不能为空");}//比如写一个map返回if(StringUtils.isBlank(userVO.getSex())){Mapresult=newHashMap<>(5);result.put("code",Constant.PARAM_FAIL_CODE);result.put("msg","性不能为空");返回结果;}//.........各种写法...userService.save(userVO);returnRspDTO.success();}这个被老大看到了,不得不说还是这样写的,被辞退了...2、什么是javax.validation?JSR303是一套JavaBean参数验证标准。它定义了许多常用的验证注解。我们可以直接在我们的JavaBean的属性中加上这些注解(面向注解编程的时代),需要验证的时候就可以验证我不会介绍SpringBoot的基础知识。推荐这个实用教程:https://github.com/javastacks...在SpringBoot中,已经包含在starter-web中,大家可以参考其他项目中的依赖,自行调整版本:javax.validationvalidation-api1.1.0.Finalorg.hibernatehibernate-validator5.2.0.Final3.注释@NotNull:不能为null,但可以为空("","","")@NotEmpty:不能为null,长度必须大于0("","")@NotBlank:只能用在String,不能为null,调用trim()后,长度必须大于0(“test”)。也就是说,它必须具有实际字符。这里只列出了HibernateValidator提供的大部分验证约束注解。其他验证约束注解和自定义验证约束注解定义请参考hibernatevalidator官方文档。4.实战练习不多说,按照练习路线走,同样使用SpringBoot的快速框架。详细代码参见:https://github.com/leaJone/mybot4.1。@Validated声明要检查的参数。这里我们在controller层做注解声明/***取参数验证注解**@paramuserDTO*@return*/@PostMapping("/save/valid")publicRspDTOsave(@RequestBody@ValidatedUserDTOuserDTO){userService.save(userDTO);返回RspDTO.success();}4.2。对参数的字段进行注解和标记@authorLiJing*@ClassName:UserDTO*@Description:用户传输对象*@date2019/7/3013:55*/@DatapublicclassUserDTOimplementsSerializable{privatestaticfinallongserialVersionUID=1L;/***用户ID*/@NotNull(message="用户ID不能为空")privateLonguserId;/**用户名*/@NotBlank(message="用户名不能为空")@Length(max=20,message="用户名不能超过20个字符")@Pattern(regexp="^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$",message="用户昵称限制:最多20个字符,包括文字、字母和数字")私有字符串用户名;/**手机号*/@NotBlank(message="手机号不能为空")@Pattern(regexp="^[1][3,4,5,6,7,8,9][0-9]{9}$",message="手机号码格式错误")privateStringmobile;/**性别*/私人字符串性别;/**email*/@NotBlank(message="联系人邮箱不能为空")@Email(message="邮箱格式不正确")privateStringemail;/**密码*/privateStringpassword;/***创建时间*/@Future(message="时间必须在未来")privateDatecreateTime;}4.3.向全局验证添加验证异常。MethodArgumentNotValidException是springBoot中绑定参数校验时的异常。需要在springBoot中进行处理。其他的需要用ConstraintViolationException处理。为了更优雅,我们将异常参数化,业务异常,统一做一个全局异常,将控制层的异常封装到我们自定义的异常中。为了更优雅,我们还做了一个统一的结构体,将请求的代码、msg、数据封装到结构体中,增加了代码的复用性。导入com.boot.lea.mybot.dto.RspDTO;导入org.slf4j.Logger;导入org.slf4j.LoggerFactory;导入org.springframework.dao.DuplicateKeyException;导入org.springframework.web.bind.MethodArgumentNotValidException;导入org。springframework.web.bind.annotation.ExceptionHandler;导入org.springframework.web.bind.annotation.RestControllerAdvice;导入org.springframework.web.servlet.NoHandlerFoundException;导入javax.validation.ConstraintViolationException;导入javax.validation.ValidationException;/***@authorLiJing*@ClassName:GlobalExceptionHandler*@Description:全局异常处理器*@date2019/7/3013:57*/@RestControllerAdvicepublicclassGlobalExceptionHandler{privateLoggerlogger=LoggerFactory.getLogger(getClass());私人静态intDUPLICATE_KEY_CODE=1001;私人静态intPARAM_FAIL_CODE=1002;privatestaticintVALIDATION_CODE=1003;/***处理自定义异常*/@ExceptionHandler(BizException.class)publicRspDTOhandleRRException(BizExceptione){logger.error(e.getMessage(),e);返回新的RspDTO(e.getCode(),e.getMessage());}/***方法参数校验*/@ExceptionHandler(MethodArgumentNotValidException.class)publicRspDTOhandleMethodArgumentNotValidException(MethodArgumentNotValidExceptione){logger.error(e.getMessage(),e);返回新的RspDTO(PARAM_FAIL_CODE,e.getBindingResult().getFieldError().getDefaultMessage());}/***ValidationException*/@ExceptionHandler(ValidationException.class)publicRspDTOhandleValidationException(ValidationExceptione){logger.error(e.getMessage(),e);返回新的RspDTO(验证码,e.getCause().getMessage());}/***ConstraintViolationException*/@ExceptionHandler(ConstraintViolationException.class)publicRspDTOhandleConstraintViolationException(ConstraintViolationExceptione){logger.error(e.getMessage(),e);返回新的RspDTO(PARAM_FAIL_CODE,e.getMessage());}@ExceptionHandler(NoHandlerFoundException.class)publicRspDTOhandlerNoFoundException(Exceptione){logger.error(e.getMessage(),e);returnnewRspDTO(404,"路径不存在,请检查路径是否正确");}@ExceptionHandler(DuplicateKeyException.class)publicRspDTOhandleDuplicateKeyException(DuplicateKeyExceptione){logger.error(e.getMessage(),e);returnnewRspDTO(DUPLICATE_KEY_CODE,"重复数据,请检查提交");}@ExceptionHandler(Exception.class)publicRspDTOhandleException(Exceptione){logger.error(e.getMessage(),e);returnnewRspDTO(500,"系统繁忙,请稍后再试");}}4.4。测试如下:在参数校验时确实返回了异常信息和对应的code,方便我们不再在ValidationMessages中处理繁琐的参数校验。properties是校验消息有一个已经写好的默认消息并支持i18n。大家可以阅读源码进行欣赏。5.自定义参数注解5.1.例如,让我们自定义一个身份证验证注解@Documented@Target({ElementType.PARAMETER,ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Constraint(validatedBy=IdentityCardNumberValidator.class)public@interfaceIdentityCardNumber{Stringmessage()默认“身份证号码无效”;类>[]组()默认{};类[]payload()default{};}该注解作用于Field字段,运行时生效,触发认证类IdentityCardNumber。message自定义的提示信息主要是从ValidationMessages.properties中提取出来的,也可以根据实际情况自定义分组。这里主要对验证人进行分类,不同的验证人操作会在不同的组中执行。有效负载主要针对bean。用的不多。5.2.然后自定义Validator,也就是真正校验的逻辑代码:.isValidate18Idcard(o.toString());}}IdCardValidatorUtils在项目源码里面,5.3可以自行查看。使用自定义注解@NotBlank(message="身份证号不能为空")@IdentityCardNumber(message="身份证信息有误,请核对提交。")privateStringclientCardNo;5.4.使用组的验证。有的宝宝说同一个物品需要重复使用。比如UserDTO在update的时候需要验证userId,但是在保存VerifyuserId的时候不需要保存,而在这两种情况下都验证username,那么使用groups:首先定义groups的分组接口Create和Update。importjavax.validation.groups.Default;publicinterfaceCreateextendsDefault{}importjavax.validation.groups.Default;publicinterfaceUpdateextendsDefault{}然后@Validated声明需要验证的验证组/***goGroups参数验证注解的组合验证**@paramuserDTO*@return*/@PostMapping("/update/groups")publicRspDTOupdate(@RequestBody@Validated(Update.class)UserDTOuserDTO){userService.updateById(userDTO);returnRspDTO.success();}在DTO的字段上定义groups={}的分组类型@DatapublicclassUserDTOimplementsSerializable{privatestaticfinallongserialVersionUID=1L;/***用户ID*/@NotNull(message="用户id不能为空",groups=Update.class)privateLonguserId;/***用户名*/@NotBlank(message="用户名不能为空")@Length(max=20,message="用户名不能超过20个字符",groups={Create.class,Update.class})@Pattern(regexp="^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$",message="用户昵称限制:最多20个字符,包括文字、字母和数字")privateString用户名;/***手机号码*/@NotBlank(message="手机号码不能为空")@Pattern(regexp="^[1][3,4,5,6,7,8,9][0-9]{9}$",message="手机号码格式不正确",groups={Create.class,Update.class})privateStringmobile;/***性别*/privateStringsex;/***Email*/@NotBlank(message="联系人邮箱不能为空")@Email(message="邮箱格式不正确")privateStringemail;/***密码*/privateStringpassword;/***创建时间*/@Future(message="Timemustbeinthefuture",groups={Create.class})privateDatecreateTime;}注意:声明的时候尽量加上extendjavax.validation.groups.Default组,否则,当你声明@Validated(Update.class)时,验证组@Email(message="Theemailformatisincorrect")会在你没有添加groups={}时默认出现,不会进行验证,因为默认的验证组是groups={Default.class}.5.5.restful风格的用法在多参数验证或者@RequestParam形式的情况下,需要在controller中添加@Validated@GetMapping("/get")publicRspDTOgetUser(@RequestParam("userId")@NotNull(message="Useridcannotbeempty")LonguserId){Useruser=userService.selectById(userId);if(user==null){returnnewRspDTO().nonAbsent("用户不存在");}returnnewRspDTO().success(user);}@RestController@RequestMapping("user/")@ValidatedpublicclassUserControllerextendsAbstractController{....Sanlo的代码...6.总结很简单use,soEasy,key参与的统一结构返回,统一参数校验,是我们代码减少大量trycatch的法宝。感觉在项目中,处理好异常,管理好异常,是一种很好的升华。文章简单,不过是菜鸟进阶笔记...这只是个人看法,技术技巧,欢迎赐教...近期热点文章推荐:1.1,000+Java面试题及答案(2022最新版本)2.太棒了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.20w程序员红包封面,快拿。..5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!