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);
}
publicSet>validateValue(ClassbeanType,StringpropertyName,Objectvalue,Class>...groups){
returnvalidator.validateValue(beanType,propertyName,value,groups);
}
/**
*校验参数,并返回第一个错误提示
*@paramt验证的对象
*@paramgroups验证的组别
*@param对象擦除前原类型
*@return第一个错误提示
*/
publicvoidvalidAndReturnFirstErrorTips(Tt,Class>...groups){
Set>validate=validator.validate(t,groups);
if(validate.size()>0){
ConstraintViolationnext=validate.iterator().next();
Stringmessage=next.getRootBeanClass().getName()+"-"+next.getPropertyPath()+"-"+next.getMessage();
thrownewValidError(message);
}
}
/**
*校验参数,并返回第一个错误提示
*@paramtargetClass验证的对象的class类型
*@paramfieldName需要验证的名字
*@paramobj需要属性值
*@paramgroups验证的组别
*@param对象擦除前原类型
*@return第一个错误提示
*/
publicvoidvalidAndReturnFirstErrorTips(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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。