在Spring MVC中,我们可以在Java Bean的字段上标注一些校验器进行字段数据合法性校验,但有时候我们需要对这些校验器进行分组操作,本文将介绍分组的作用及使用。

试想一种情况,我们有一个 AccountInfo 类用来存放用户的各种信息:

public class AccountInfo
{
    @NotNull
    private Long accountId;

    @NotNull
    private String username;
     
    @NotNull
    private String password;
     
    // constructors, getters, setters, etc. down here
}

 

现在,在我们的业务上有两个操作,第一个:修改用户名,第二个:修改密码,前后端数据交互上我们使用 json,所以在 Controller 中有如下代码:

@RequestMapping(value="/changeUserName", method=RequestMethod.POST)
@ResponseBody
public Result<String> create(@RequestBody @Valid AccountInfo vo, BindingResult bindingResult) {
	Result<String> result = new Result<>(false);
	if (bindingResult.hasErrors()) {
		result.setMsg(bindingResult.getAllErrors().get(0).getDefaultMessage());
			
	} else {
		biz.changeUserName(vo);
		result.setStatus(true);
	}
	return result;
}

@RequestMapping(value="/changePassword", method=RequestMethod.POST)
@ResponseBody
public Result<String> create(@RequestBody @Valid AccountInfo vo, BindingResult bindingResult) {
	Result<String> result = new Result<>(false);
	if (bindingResult.hasErrors()) {
		result.setMsg(bindingResult.getAllErrors().get(0).getDefaultMessage());
			
	} else {
		biz.changePassword(vo);
		result.setStatus(true);
	}
	return result;
}

运行程序分别调用这两个方法,你就会蛋疼的发现提交的 Json 中只要被 @NotNull 标注的字段都需要传递,而我仅仅只是为了修改一下用户名(或者密码)。

在之前,我没有特别关注这种问题有没有好的解决方法,于是乎一直使用一种折中的方案,就是仅对某些任何操作都需要校验的字段进行标注,而剩余的其他字段在业务逻辑中进行一些合法性判断。比如上述 AccountInfo 中的 accountId,在修改用户名和修改密码的时候都需要传递以定位用户记录,所以该字段进行标注,而 password 不标注,直接在 bizif 判断,这将导致业务层出现许多业务不相关的字段合法性判断代码。

解决方案是使用校验器分组(Validator Group),我们可以在校验器上分配一个或多个分组标识,然后在控制层指定哪些组标识的校验器需要执行即可解决我们上面的问题了:

public class AccountInfo
{
 
    @NotNull
    private Long accountId;

    @NotNull(groups=ChangeUserName.class)
    private String username;
     
    @NotNull(groups=ChangePassword.class)
    private String password;
     
    // constructors, getters, setters, etc. down here

    /**
     * 修改用户名操作字段校验器组标识接口
     * @author Pacey
     * @version 2016年5月12日 下午2:13:53
     * 
     */
    public interface ChangeUserName{};

    /**
     * 修改密码操作字段校验器组标识接口
     * @author Pacey
     * @version 2016年5月12日 下午2:13:53
     * 
     */
    public interface ChangePassword{};
}

控制器:

@RequestMapping(value="/changeUserName", method=RequestMethod.POST)
@ResponseBody
public Result<String> create(@RequestBody @Validated({Default.class, ChangeUserName.class}) AccountInfo vo, BindingResult bindingResult) {
	Result<String> result = new Result<>(false);
	if (bindingResult.hasErrors()) {
		result.setMsg(bindingResult.getAllErrors().get(0).getDefaultMessage());
			
	} else {
		biz.changeUserName(vo);
		result.setStatus(true);
	}
	return result;
}

可以看到,在控制器中,我们将原先的 @Valid 注解更换为 @Validated,并且使用一个类数组配置了该注解,这个数组中的每个类就是指定要执行的校验器的组标识。

注意,我们这里还指定了一个叫 Default 的组,这个组是默认的校验器组,如果你的校验器没有指定任何的校验器组,那么默认他会使用该组,比如上文中的 accountId,我们没有为他指定任何自定义的分组标识,那么他是属于 Default 组的,我们必须在控制器中手动指定 Default 组,否则任何没有组标识的校验器都不会执行。同理,如果在控制器中我们没有指定任何的校验器组,那么默认的 Default 组校验器将会被执行,而任何只标注了其他组标识的校验器将不会被执行

Spring MVC 全局异常处理

经典的 MVC 程序中,如果产生了一个非业务异常,或者非本业务可以处理的其他业务异常,那么我们一般会一直往上层抛(或者适当包装后继续抛出)直到控制层,控...

阅读全文

Spring中多线程定时计划任务

web应用程序中时常需要定时循环的执行一些状态更新,或者数据清理的相关任务,亦如超时订单的处理,spring中实现计划任务也是非常方便 Java代码 public clas...

阅读全文

欢迎留言