Java使用@Validated注解进行参数验证的方法
目前项目中大部分代码进行参数验证都是写代码进行验证,为了提升方便性和代码的简洁性,所以整理了下使用注解进行参数验证。使用效果如下:
//要验证的实体类
@Data
publicclassUserimplementsSerializable{
@NotBlank(message="id不能为空!",groups=Update.class)
protectedStringid="";
@NotBlank(message="商户id不能为空!")
protectedStringtenantId;
@NotBlank(message="名称不能为空!")
protectedStringname="";
}
//controller
//在要验证的参数上添加@Validated注解即可
@PostMapping("add")
publicbooleanaddUser(@Validated@RequestBodyUseruser){
ProdAllotStandardinfo=allotStandardService.getProdAllotStandardWithDetailById(id);
returninfo;
}
//修改则需要比添加的基础上多验证一个id,可以通过group的方式进行区分
//实体类上不设置groups,默认会是Default,所以修改的时候,我们需要指定Update和Default这两个组
//group是可以自定义的,我默认定义了Add,Update,Delete,Search这四个组
@PostMapping("update")
publicbooleanupdateUser(@Validated({Update.class,Default.class})@RequestBodyUseruser){
ProdAllotStandardinfo=allotStandardService.getProdAllotStandardWithDetailById(id);
returninfo;
}
//当然该注解也支持service上实现,同样的使用方法,在service的实现类的参数上加上对应注解即可,如:
publicbooleanaddUser(@ValidatedUseruser){
}
//通过不同group的搭配,我们就可以灵活的在实体类上配置不同场景的验证了
//group为一个接口,用以下方式创建
publicinterfaceAdd{
}
下面看一下具体的实现,验证框架的核心实现采用的是hibernate-validator,我采用的是5.4.3.Final版本。
controller和service的实现方式不同,下面分别介绍下。
不管哪种实现,我们都需要先定义一个异常和一个异常拦截器,当参数校验出现问题时,我们就抛出对应异常。
//为了简短,省略了部分代码
publicclassParamsExceptionextendsRuntimeException{
privateStringcode;
publicBusinessException(){
}
publicParamsException(Stringcode,Stringmessage){
super(message);
this.code=code;
}
publicParamsException(Stringmessage){
super(message);
}
}
//定义异常处理类
@ControllerAdvice
publicclassExceptionAdvice{
privatestaticLoggerL=LoggerFactory.getLogger(ExceptionAdvice.class);
//对所有的ParamsException统一进行拦截处理,如果捕获到该异常,则封装成MessageBody返回给前端
@ExceptionHandler(value=ParamsException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
publicMessageBodyhandleParamsException(HttpServletRequestrequest,BusinessExceptione){
L.error(e.getMessage(),e);
returngetErrorMessageBody(e.getData(),e.getMessage(),e.getMessage(),
StringUtils.isEmpty(e.getCode())?ResponseCode.BUSINESS_ERROR:e.getCode());
}
privateMessageBodygetErrorMessageBody(Objectdata,Stringmessage,StringerrorInfo,Stringcode){
MessageBodybody=newMessageBody();
body.setCode(code);
body.setData(data);
body.setErrorCode(Integer.parseInt(code));
body.setErrorInfo(errorInfo);
body.setMessage(message);
body.setSuccess(false);
returnbody;
}
}
controller的验证的实现:
主要是通过实现springmvc给我们留下的接口进行实现的,该方案没有用到反射和代理。
1. 实现spring提供的SmartValidator接口
publicclassParamsValidatorimplementsSmartValidator{
privatejavax.validation.Validatorvalidator=Validation.buildDefaultValidatorFactory().getValidator();
@Override
publicbooleansupports(Class>clazz){
returntrue;
}
//注解上没有有group的验证逻辑
@Override
publicvoidvalidate(Objecttarget,Errorserrors){
validate(target,errors,null);
}
//注解上带有group的验证逻辑
//第一个参数为我们要验证的参数,第二个不用管,第三个为注解上设置个groups
@Override
publicvoidvalidate(Objecttarget,Errorserrors,Object...validationHints){
//这里面为验证实现,可以根据自己的需要进行完善与修改
if(target==null){
thrownewParamsException("参数不能为空!");
}else{
if(targetinstanceofString){
if(StringUtils.isEmpty(target)){
thrownewParamsException("参数不能为空!");
}
}elseif(targetinstanceofCollection){
for(Objecto:(Collection)target){
validate(o,validationHints);
}
}else{
validate(target,validationHints);
}
}
}
privatevoidvalidate(Objecttarget,Object...objs){
Set>violations;
//没有groups的验证
if(objs==null||objs.length==0){
violations=validator.validate(target);
}else{
//基于groups的验证
Set>groups=newLinkedHashSet>();
for(Objecthint:objs){
if(hintinstanceofClass){
groups.add((Class>)hint);
}
}
violations=validator.validate(target,ClassUtils.toClassArray(groups));
}
//若为空,则验证通过
if(violations==null||violations.isEmpty()){
return;
}
//验证不通过则抛出ParamsException异常。
for(ConstraintViolationitem:violations){
thrownewParamsException(item.getMessage());
}
}
}
2.配置并设置Validator验证器
@Configuration
publicclassWebMvcConfigurerextendsWebMvcConfigurerAdapter{
//我们在这里重写spring的一个方法,返回我们自定义的验证器
@Override
publicValidatorgetValidator(){
returncreateValidator();
}
@Bean
publicParamsValidatorcreateValidator(){
returnnewParamsValidator();
}
}
非常简单,通过上面配置,就可以在controller上使用注解了。
springmvc对这个注解的处理主要是在RequestResponseBodyMethodProcessor这个类中的resolveArgument方法实现的,该类主要处理方法的参数和返回值。
springmvc调用的一段代码。
protectedvoidvalidateIfApplicable(WebDataBinderbinder,MethodParameterparameter){
Annotation[]annotations=parameter.getParameterAnnotations();
for(Annotationann:annotations){
ValidatedvalidatedAnn=AnnotationUtils.getAnnotation(ann,Validated.class);
if(validatedAnn!=null||ann.annotationType().getSimpleName().startsWith("Valid")){
Objecthints=(validatedAnn!=null?validatedAnn.value():AnnotationUtils.getValue(ann));
Object[]validationHints=(hintsinstanceofObject[]?(Object[])hints:newObject[]{hints});
binder.validate(validationHints);
break;
}
}
}
service的验证的实现:
该方案主要通过aop进行拦截处理的。如下配置:
@Component
@Aspect
publicclassParamsValidateAdvice{
//这里重用上面定义的那个validator
privateParamsValidatorvalidator=newParamsValidator();
/**
*拦截参数上加了@Validated的注解的方法
*排除掉controller,因为controller有自己的参数校验实现不需要aop
*/
@Pointcut("execution(*com.choice..*(..,@org.springframework.validation.annotation.Validated(*),..))&&"+
"!execution(*com.choice..api..*(..))&&"+
"!execution(*com.choice..controller..*(..))")
publicvoidpointCut(){}
@Before("pointCut()")
publicvoiddoBefore(JoinPointjoinPoint){
Object[]params=joinPoint.getArgs();
MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();
Methodmethod=signature.getMethod();
Parameter[]parameters=method.getParameters();
//验证参数上的注解
for(inti=0;i0){
validator.validate(params[i],null,validated.value());
}else{
validator.validate(params[i],null);
}
}
}
}
这样就可以在service使用验证注解了,具体的Pointcut可以自己进行配置。
个人觉得参数校验在controller或者对外暴露的服务中去做就好了,因为这些都是对外提供服务的,controller层也应该去做这些,所以参数需要校验好。
没必要在自己内部调用的service中加校验。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。