关于spring web的参数校验,其过程是通过org.springframework.validation.DataBinder,调用org.springframework.validation.Validator下的validate(Object target, Errors errors)方法。该Validator的实例为org.springframework.validation.beanvalidation.SpringValidatorAdapter。该类内部绑定了javax.validation.Validator的一个实例,一般为org.hibernate.validator.internal.engine.ValidatorImpl。最终整个校验结果被存储在org.springframework.validation.BindingResult的实例中。
Controller中关于bindingResult的用法,官网上有一个入门示例.
其中关于参数校验异常的处理是
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
1
2
3
4
这就受不了了,难道要在每一个带有@Valid的@RequestMapping都这么if…else…一下?好丑…
怎么解决呢?这个时候就需要强大的AOP(切面)了。切面的概念和Spring集成AOP的方法就不在这里介绍了,请读者自行补充。
那首先来简化官网示例的模型。
需要验证的类为Info:
public class Info{
@NotBlank(message="输入信息不能为空")
private String s;
}
1
2
3
4
Web Controller为:
@Controller
public class WebController{
@RequestMapping(value="/", method=RequestMethod.POST)
public Response checkPersonInfo(@Valid Info info, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return doErrorHandle();
}
return doNormalHandle();
}
}
1
2
3
4
5
6
7
8
9
10
好了,开始加入切面方法吧!
切面的实现为:
@Aspect
@Component
public class ControllerValidatorInterceptor {
@Around("execution(* com.aerexu.web.*.*(..)) && args(..,bindingResult)")
public Object doAround(ProceedingJoinPoint pjp, BindingResult bindingResult) throws Throwable {
Object retVal;
if (bindingResult.hasErrors()) {
retVal = doErrorHandle();
} else {
retVal = pjp.proceed();
}
return retVal;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
对应的Web Controller可以简化为:
@Controller
public class WebController{
@RequestMapping(value="/", method=RequestMethod.POST)
public Response checkPersonInfo(@Valid Info info, BindingResult bindingResult) {
return doNormalHandle();
}
}
1
2
3
4
5
6
7
其中,execution(* com.aerexu.web.*.*(..))限定了切面只会尝试切com.aerexu.web包中的方法,args(..,bindingResult)则限定了方法的最后一个参数是BindingResult类型且引用会传递给doAround(ProceedingJoinPoint pjp, BindingResult bindingResult)的bindingResult。
当bindingResult含有错误时,则执行doErrorHandle()并返回结果,controller中的方法doNormalHandle()并不会被执行。
利用这种方式,Controller内部就可以专注于业务处理,参数验证异常的处理完全独立于切面中了。
如有其它好的实现方式,欢迎讨论。