背景
使用注解参数校验,可避免业务代码中无休止的参数校验判断,在分层的方面来说,参数校验都是在Controller层完成的,那么Spring MVC中,可直接添加Validate相关的参数校验注解,即可快速完成而无需其它额外的配置,但是如果想在Service层的接口中添加参数校验,则需要额外的配置,否则,注解添加后是无效的。
实现
我们了解到参数校验是基于Validator来做的,首先需要添加hibernate-validator和validation-api依赖,由于spring-boot-starter-web依赖hibernate-validator,而hibernate-validate又依赖validation-api,所以项目中只需要添加spring-boot-starter-web依赖即可,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
此时,需要在切面中通过Validator完成接口调用时的参数校验,实现如下:
@Aspect@Componentpublic class ParamsCheckAspect { private static Validator validator; static { validator = Validation.byDefaultProvider().configure() .messageInterpolator(new ResourceBundleMessageInterpolator( new PlatformResourceBundleLocator("validationMessages"))) //手动指定校验提示资源(默认在resource目录下ValidationMessages.properties) .buildValidatorFactory().getValidator(); } // 定义接口参数校验切入点 @Pointcut("@annotation(org.springframework.validation.annotation.Validated))") private void validateMethod() { } @Before("validateMethod()") public void before(JoinPoint joinPoint) throws CheckedException { Object[] args = joinPoint.getArgs(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 执行方法参数的校验 Set<ConstraintViolation<Object>> constraintViolations = validator.forExecutables().validateParameters(joinPoint.getThis(), signature.getMethod(), args); List<String> messages = Lists.newArrayList(); for (ConstraintViolation<Object> error : constraintViolations) { messages.add(error.getMessage()); } if(!messages.isEmpty()){ throw new CheckedException(JSONObject.toJSONString(messages)); } } }
代码解析:
- 在static块中,完成Validator的初始化;
- validateMethod是定义切点,通过PointCut注解可以看出,只有添加了Validated注解的方法被调用时,才会执行参数校验,此举是为了减少没有参数校验注解的方法也要执行校验的过程,避免性能浪费。
- 由于参数校验要先于方法的执行,所以,通过Before注解,执行前置执行,在该切面方法中完成参数的校验。
使用示例
在完成校验的逻辑后,接下来就是使用示例,使用时需要注册基本参数注解校验和POJO类型的参数校验,需要校验POJO时,需要添加Valid注解,其它的基本参数注解则正常使用即可,如下:
@Validatedpublic int saveUser(@Min(value=1, message="{a.b.c}") int operUserId, @NotNull(message = "{x.y.z}") @Valid User user);
代码解析:
- 首先方法实现需要添加@Validated注解,否则,上一步中配置的切面不会被执行,从而也不会进行参数校验;
- 示例中分为基本参数校验和POJO校验,需要注意的是message注解属性的格式,这里硬编码也可以,但是使用占位符的方式更加方便配置和复用,此处a.b.c和x.y.z需要在上一步的validationMessages中定义好;
- 需要注意的是POJO需要额外添加@Valid注解,才能对POJO中的属性进行校验;
- 特别注意:1中的注解要写在实现类上,2、3中的注解需要写在接口上,实现类中写不写都可以,否则会出错,错误可自行研究。