Springboot 使用 JSR 303 对 Controller 控制层校验及 Service 服务层 AOP 校验 使用消息资源文件对消息国际化
导包和配置
导入JSR303的包、hibernatevalid的包
org.hibernate.validator hibernate-validator 6.0.5.Final javax.validation validation-api 2.0.0.Final
springboot配置
resources/application.yml消息资源文件国际化处理配置
spring:
messages:
basename:base,todo#资源文件base.properties和todo.properties,多个用逗号隔开
encoding:UTF-8#必须指定解析编码,否则中文乱码
在springboot启动类里面配置
@SpringBootApplication publicclassApplicationextendsWebMvcConfigurerAdapter{ @Value("${spring.messages.basename}") privateStringbasename; publicstaticvoidmain(String[]args){ SpringApplication.run(Application.class,args); } @Bean @Primary publicMessageSourcemessageSource(){ ResourceBundleMessageSourceresourceBundleMessageSource=newResourceBundleMessageSource(); resourceBundleMessageSource.setUseCodeAsDefaultMessage(false); resourceBundleMessageSource.setDefaultEncoding("UTF-8");//重复定义 resourceBundleMessageSource.setBasenames(basename.split(",")); returnresourceBundleMessageSource; } @Bean @Primary publicLocalValidatorFactoryBeanvalidator(){ LocalValidatorFactoryBeanvalidatorFactoryBean=newLocalValidatorFactoryBean(); validatorFactoryBean.setProviderClass(HibernateValidator.class); validatorFactoryBean.setValidationMessageSource(messageSource()); returnvalidatorFactoryBean; } @Override publicValidatorgetValidator(){ returnvalidator(); } /** *方法级别的单个参数验证开启 */ @Bean publicMethodValidationPostProcessormethodValidationPostProcessor(){ returnnewMethodValidationPostProcessor(); } }
我们对于校验参数通过不了抛出的异常进行处理,是通过统一异常捕捉。
@ControllerAdvice @Component publicclassBindValidExceptionHandler{ @ResponseStatus(value=HttpStatus.OK) @ExceptionHandler(ConstraintViolationException.class) public@ResponseBody MsghandleConstraintViolationException(ConstraintViolationExceptione){ StringmessageTemplate=e.getConstraintViolations().iterator().next().getMessageTemplate(); returnMsg.error(messageTemplate); } @ResponseStatus(value=HttpStatus.OK) @ExceptionHandler(BindException.class) public@ResponseBody MsghandleBindException(BindExceptione){ BindingResultbindingResult=e.getBindingResult(); StringclassName=bindingResult.getTarget().getClass().getName(); FieldErrornext=bindingResult.getFieldErrors().iterator().next(); StringfieldName=next.getField(); StringdefaultMessage=next.getDefaultMessage(); if(Pattern.compile("IllegalArgumentException:Noenum").matcher(defaultMessage).find()){ Matchermatcher=Pattern.compile("forvalue'(.*?)'").matcher(defaultMessage); if(matcher.find()){ defaultMessage="找不到枚举类型【"+matcher.group(1)+"】"; } } returnMsg.error(defaultMessage); } @ResponseStatus(value=HttpStatus.OK) @ExceptionHandler(ValidError.class) public@ResponseBody MsghandleValidError(ValidErrore){ returnMsg.error(e.getMessage()); } }
resources/base.propertie
creatorId=创建者id不能为小于{value}。
modifierId=修改者id不能为小于{value}。
resources/todo.properties
todo.privateId.min=私有id不能为小于{value}。
在bean字段上使用注解,其中group中的C和S接口是指Controller和Service的叫法简称,里面分别有Insert接口、Update接口等等,都是自定义约定的东西。
/** *私有id,是代表项目任务/非项目任务/风险/问题/评审待办问题等多张表的外键 */ @Min(value=1,message="{todo.privateId.min}",groups={C.Insert.class,C.Update.class,S.Insert.class,S.Update.class}) privatelongprivateId; /** *创建者id */ @Min(value=1,message="{creatorId}",groups={S.Insert.class}) privatelongcreatorId; Controller控制层验证 @Validated @RestController @RequestMapping("todo") publicclassTodoController{ @Autowired privateTodoServicetodoService; @GetMapping("getVo") publicMsggetVo( @Min(value=1,message="待办id不能小于1。") @RequestParam(required=false,defaultValue="0") longid ){ returnthis.todoService.getVo(id); } @PostMapping("add") publicMsgadd(@Validated({C.Insert.class})Todotodo){ returnthis.todoService.add(todo); } }
@Validated({C.Insert.class})声明启用bean注解上的验证组,其他验证组不会进行验证,这样可以区别开来进行单独验证。
而像没有实体,只有一个基础数据类型的,可以进行验证,但是需要满足三个条件:
- 在启动类配置方法级别验证启用类
- 在Controller类上注解@Validated
- 在方法参数里使用验证注解如@Min,@NotNull等等
自行验证。
Service服务层AOP验证
ValidUtil工具类
需要被springboot扫描并注册为单例
@Component publicclassValidUtil{ @Autowired privateValidatorvalidator; publicSet >validate(Tobject,Class>...groups){ returnvalidator.validate(object,groups); } public Set >validateValue(Class beanType,StringpropertyName,Objectvalue,Class>...groups){ returnvalidator.validateValue(beanType,propertyName,value,groups); } /** *校验参数,并返回第一个错误提示 *@paramt验证的对象 *@paramgroups验证的组别 *@param 对象擦除前原类型 *@return第一个错误提示 */ public voidvalidAndReturnFirstErrorTips(Tt,Class>...groups){ Set >validate=validator.validate(t,groups); if(validate.size()>0){ ConstraintViolation next=validate.iterator().next(); Stringmessage=next.getRootBeanClass().getName()+"-"+next.getPropertyPath()+"-"+next.getMessage(); thrownewValidError(message); } } /** *校验参数,并返回第一个错误提示 *@paramtargetClass验证的对象的class类型 *@paramfieldName需要验证的名字 *@paramobj需要属性值 *@paramgroups验证的组别 *@param 对象擦除前原类型 *@return第一个错误提示 */ public voidvalidAndReturnFirstErrorTips(ClasstargetClass,StringfieldName,Objectobj,Class>...groups){ Set >validate=validator.validateValue(targetClass,fieldName,obj,groups); if(validate.size()>0){ Stringmessage=targetClass.getName()+"-"+fieldName+"-"+validate.iterator().next().getMessage(); thrownewValidError(message); } } }
AOP配置
主要原理是利用aop拦截方法执行参数,对参数获取注解。再利用工具类来验证参数,如果验证不通过,直接抛出自定义错误,自定义错误已经全局统一处理了。
@Aspect @Component publicclassValidatorAOP{ @Autowired privateValidUtilvalidUtil; /** * 定义拦截规则:拦截com.servic 包下面的所有类中,有 @Service 注解的方法。 */ @Pointcut("execution(*com.service..*(..))and@annotation(org.springframework.stereotype.Service)") publicvoidcontrollerMethodPointcut(){ } /** * 拦截器具体实现 */ @Around("controllerMethodPointcut()")// 指定拦截器规则;也可以直接把 “execution(*com.xjj.........)” 写进这里 publicObjectInterceptor(ProceedingJoinPointpjp){ MethodSignaturemethodSignature=(MethodSignature)pjp.getSignature(); Methodmethod=methodSignature.getMethod(); Annotation[][]argAnnotations=method.getParameterAnnotations(); Object[]args=pjp.getArgs(); for(inti=0;i[]groups=validated.value(); validUtil.validAndReturnFirstErrorTips(args[i],groups); } } } try{ returnpjp.proceed(args); }catch(Throwablethrowable){ throwable.printStackTrace(); } returntrue; } }
验证注解@Min@NotNull使用方法
不能写在实现类上,只能在接口中使用注解
与Controller使用方式基本一样
@Validated publicinterfaceTodoService{ /** *查询单个待办 *@paramid序号 *@return单个待办 */ MsggetVo(@Min(value=1,message="待办id不能小于1。")longid); /** *添加数据 *@paramtodo对象 */ Msgadd(@Validated({S.Insert.class})Todotodo); }
分享几个自定义验证注解
字符串判空验证
packagejavax.validation.constraints; importjavax.validation.Constraint; importjavax.validation.ConstraintValidator; importjavax.validation.ConstraintValidatorContext; importjavax.validation.Payload; importjava.lang.annotation.*; /** *字符串判空验证,hibernate自带的可能有问题,使用不了,需要重写,package是不能变的。 */ @Documented @Constraint( validatedBy={NotBlank.NotBlankValidator.class} ) @Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public@interfaceNotBlank{ Class>[]groups()default{}; Stringmessage()default"{notBlank}"; Class[]payload()default{}; classNotBlankValidatorimplementsConstraintValidator{ publicNotBlankValidator(){ } @Override publicvoidinitialize(NotBlankconstraintAnnotation){ } @Override publicbooleanisValid(Objectvalue,ConstraintValidatorContextcontext){ returnvalue!=null&&!value.toString().isEmpty(); } } }
类型判断,判断type是否为其中一个值,可以根据验证组自定义判断
resources/todo.properties todo.todoType.insert=新增时,待办类型只能是非项目任务、项目任务、问题之中一。 todo.todoType.update=修改时,待办类型只能是风险、评审待办问题之中一。 bean /** *待办类型0非项目任务1项目任务2问题3风险4评审待办问题 */ @TodoTypeValid(value={"0","1","2"},message="{todo.todoType.insert}",groups={C.Insert.class,S.Insert.class}) @TodoTypeValid(value={"3","4"},message="{todo.todoType.update}",groups={C.Update.class,S.Update.class}) privateStringtodoType;
自定义注解
@Documented @Constraint(validatedBy={TodoTypeValid.TodoTypeValidFactory.class}) @Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(TodoTypeValid.List.class) public@interfaceTodoTypeValid{ Stringmessage()default"请输入正确的类型"; String[]value()default{}; Class>[]groups()default{}; Class[]payload()default{}; classTodoTypeValidFactoryimplementsConstraintValidator{ privateString[]annotationValue; @Override publicvoidinitialize(TodoTypeValidtodoStatusValid){ this.annotationValue=todoStatusValid.value(); } @Override publicbooleanisValid(Stringvalue,ConstraintValidatorContextcontext){ if(Arrays.asList(annotationValue).contains(value)) returntrue; returnfalse; } } @Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @interfaceList{ TodoTypeValid[]value(); } }
@Repeatable(TodoTypeValid.List.class)是JDK8支持的同一注解多次特性。
根据上面的同样也可以用在枚举类上
resources/todo.properties todo.todoStatus.insert=新增时,状态只能是未开始。 todo.todoStatus.update=修改时,状态只能是进行中或已完成。 bean /** *待办状态0未开始1进行中2已完成 */ @TodoStatusValid(enums={TodoStatus.NOT_STARTED},message="{todo.todoStatus.insert}",groups={C.Insert.class,S.Insert.class}) @TodoStatusValid(enums={TodoStatus.PROCESSING,TodoStatus.COMPLETED},message="{todo.todoStatus.update}",groups={C.Update.class,S.Update.class}) privateTodoStatustodoStatus;
自定义注解
@Documented @Constraint(validatedBy={TodoStatusValid.TodoStatusValidFactory.class}) @Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(TodoStatusValid.List.class) public@interfaceTodoStatusValid{ Stringmessage()default"请输入正确的状态"; TodoStatus[]enums()default{}; Class>[]groups()default{}; Class[]payload()default{}; classTodoStatusValidFactoryimplementsConstraintValidator{ privateTodoStatus[]enums; @Override publicvoidinitialize(TodoStatusValidtodoStatusValid){ this.enums=todoStatusValid.enums(); } @Override publicbooleanisValid(TodoStatusvalue,ConstraintValidatorContextcontext){ TodoStatus[]values=TodoStatus.values(); if(enums!=null&&enums.length!=0){ values=enums; } if(Arrays.asList(values).contains(value)) returntrue; returnfalse; } } @Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @interfaceList{ TodoStatusValid[]value(); } }
总结
以上所述是小编给大家介绍的Springboot使用JSR303对Controller控制层校验及Service服务层AOP校验使用消息资源文件对消息国际化,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。