java校验的优化

258 阅读2分钟

以往的校验方式,通过在代码里写各种非空的判断,各种长度的限制,

例: private void validate(SysUser user){

        if (StringUtils.isEmpty(user.getUserName())) {

            throw new BusinessException(UserServiceError.SERVICE_CHECK_NAME_NULL.value(), UserServiceError.SERVICE_CHECK_NAME_NULL.displayName());

        }

        if (user.getUserName().length() > Constant.SIXTY) {

            throw new BusinessException(UserServiceError.SERVICE_CHECK_NAME_LENGTH.getCode(), UserServiceError.SERVICE_CHECK_NAME_LENGTH.getMessage());

        }

        if (user.getAge()==null ) {

            throw new BusinessException(UserServiceError.SERVICE_CHECK_AGE_NOT_NULL.getCode(), UserServiceError.SERVICE_CHECK_AGE_NOT_NULL.getMessage());

        }

}

现在我们将通用为空、长度限制的校验抽取出来,通过注解的形式独立出来,如:

@NotBlank(message = "名称不能为空")

@Size(min = 1, max = 30, message = "名称长度必须在1-30之间")

然后按规则对注解进行解析,这个过程JSR、Hibernate已经做了,引过来加以使用

      org.springframework.boot

      spring-boot-starter-validation

定义实体类,在字段上用hibernate定义的注解标识非空及长度限制,如:

@Data

public class SysUser implements Serializable {

/**

 *用户ID

 */

 private Long userId;

 /**

  *名称

  */

    @NotBlank(message = "名称不能为空")

    @Size(min = 1, max = 30, message = "名称长度必须在1-30之间")

    private String username;

/**

*密码

*/

     private String password;

}

在接口中使用校验

public Result save(SysUser dto){

     //效验数据

     ValidatorUtils.validate(dto, Default.class);

     sysUserService.save(dto);

     return new Result();

}

Validator工具类封装

@Component

public class ValidatorUtils {

    @Autowired

    private Validator validator;

    /**

     * 校验对象

     */

    public void validate(Object object, Class<?>... groups)

            throws ValidatorException {

        Set<ConstraintViolation> constraintViolations = validator.validate(object, groups);

        if (!constraintViolations.isEmpty()) {

            ConstraintViolation constraint = constraintViolations.iterator().next();

            throw new ValidatorException(constraint.getMessage());

        }

    }

}

以上这种方式,对业务的侵入较大,可以进行优化

实体类定义保持不变

@Data

public class SysUser implements Serializable {

/**

 *用户ID

 */

 private Long userId;

 /**

  *名称

  */

    @NotBlank(message = "名称不能为空")

    @Size(min = 1, max = 30, message = "名称长度必须在1-30之间")

    private String username;

/**

*密码

*/

     private String password;

}

定义注解,将需要进行校验的模块用该注解标识,去除了手动调用ValidatorUtils的烦恼,

@Target({ElementType.METHOD,ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface Validate {

    public Class[] value() default Default.class;

}

定义切面,在定义注解Validate的地方统一进行校验处理,

@Component

@Aspect

public class ValidateAspect {

    @Autowired

    private Validator validator;

    /**

     * 配置切入点

     */

    @Pointcut("@annotation(com.example.validate.annotation.Validate)")

    public void logPointcut() {

    }

    @Around("logPointcut()")

    public Object paramsAdvice(ProceedingJoinPoint joinPoint) {

        try {

            Object[] args = joinPoint.getArgs();

            if (args == null || args.length == 0) {

                return joinPoint.proceed();

            } else {

                for (Object arg : args) {

                    try {

                        Set<ConstraintViolation> validate = validator.validate(arg, Default.class);

                        if (!validate.isEmpty()) {

                            StringBuilder stringBuilder = new StringBuilder();

                            validate.iterator().forEachRemaining((value) -> {

                                stringBuilder.append(value.getMessage());

                            });

                            if(!StringUtils.isEmpty(stringBuilder.toString())){

                                throw new IllegalArgumentException(stringBuilder.toString());

                            }

                        }

                    } catch (Exception e) {

                        throw e;

                    }

                }

            }

            return joinPoint.proceed();

        } catch (Throwable e) {

            throw new IllegalArgumentException(e.getMessage());

        }

    }

}

接口中仅需使用@Validate,即可实现自动校验

@PostMapping(value = "/user")

@Validate

public String addUser(@RequestBody SysUser sysUser){

    return "ok";

}