数据校验不建议放在 Controller 层或是 Service 层中做,而是建议放在 POJO 中,可以利用 Java Bean Validation 标准提供的一系列注解去做,配合 @Valid` 和 @Validation` 使用。

业务相关的数据校验也应该交给 Bean Validation,可以通过自定义注解的方式来实现,将业务校验逻辑与业务处理逻辑分离,并防止各层重复校验、或者分别校验一部分的情况出现。

/**
 * 表示一个用户的信息是无冲突的
 *
 * “无冲突”是指该用户的敏感信息与其他用户不重合,譬如将一个注册用户的邮箱,修改成与另外一个已存在的注册用户一致的值,这便是冲突
 **/
@Documented
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
@Constraint(validatedBy = AccountValidation.NotConflictAccountValidator.class)
public @interface NotConflictAccount {
    String message() default "用户名称、邮箱、手机号码与现存用户产生重复";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
pubcli class NotConflictAccountValidator implements
        ConstraintValidator<NotConflictAccount, String> {
 
    @Override
    public void initialize(NotConflictAccount constraintAnnotation) {
    
    }
 
    @Override
    public boolean isValid(Account c,
            ConstraintValidatorContext constraintValidatorContext) {
        Collection<Account> collection =             
            repository.findByUsernameOrEmailOrTelephone(
            c.getUsername(), c.getEmail(), c.getTelephone());
            // 将用户名、邮件、电话改成与现有完全不重复的,或者只与自己重复的,就不算冲突
            return collection.isEmpty() || (collection.size() == 1 && collection.iterator().next().getId().equals(c.getId()));
    }
    
}

建议将不带业务含义的格式校验注解放到 Bean 的类定义里面(类的成员变量上),把带业务逻辑的校验放到类定义的外面(像是 @NotConflictAccount Account account)。防止业务校验的执行频率过高,影响性能。