首先,咱们达成如下共识:html
@Data public class UserVo { private String username; private Integer age; private List<String> hobby; }
@Service public class UserService { public void addUser(UserVo userVo) { // 参数基本校验 if (StringUtils.isEmpty(userVo.getUsername()) || userVo.getAge() < 0 || userVo.getAge() > 200 || CollectionUtils.isEmpty(userVo.getHobby())) { throw new IllegalArgumentException(); } // 业务逻辑操做 } }
有了上述共识以后,咱们来开始实现一点想法:结合Spring的切面用法,可以很方便的拿到切点的方法入参,而后能够进行参数校验。java
比较常见的一种方式是:使用Java bean注解校验。大体用法就是:在pojo加上相应的校验注解,而后在切面中利用hibernate-validator
进行校验。这种方式springmvc已经帮咱们实现了,并且使用效果不错。spring
@Data public class UserVo { @NotEmpty private String username; @Max(value = 200) @Min(value = 1) private Integer age; @NotEmpty private List<String> hobby; }
前面提到,暴露的方法尽可能简洁易用,不要形成太多的干扰。pojo的属性应该保持简洁,加上必要的注释。设计模式
咱们本来的想法就是,利用切面把方法中大量相似的简单的校验逻辑抽离出来,在切面中执行校验的过程。不一样pojo有不一样的属性,那么校验逻辑仍是会存在不一样,若是不使用注解校验,在切面中拿到了不一样的pojo,可能会写不少的 instance of判断,固然也能够利用设计模式实现的很好。总之,咱们确实把service方法中的校验抽离出来了。mvc
铺垫了这么多,咱们为何不能够把校验逻辑放进对应的pojo中呢?这样,调用方也可以清晰地看到基本的校验逻辑,其调用前也能够调用校验方法检查参数的合法性。框架
public interface ValidationHandler { /** * 校验pojo的属性 * * @return 经过/不经过 */ boolean isValid(); }
ValidationHandler
,编写校验逻辑@Data public class UserVo implements ValidationHandler { private String username; private Integer age; private List<String> hobby; @Override public boolean isValid() { return StringUtils.isNotEmpty(username) && age > 0 && age < 100 && !hobby.isEmpty(); } }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ParamValidation { }
@Service public class UserService { @ParamValidation public void addUser(UserVo userVo) { // 业务逻辑操做 } }
@Component @Aspect public class ParamValidator { @Pointcut("@annotation(com.ex.validator.ParamValidation)") public void validate() { } @Before("validate()") public void before(JoinPoint joinPoint) { for (Object arg : joinPoint.getArgs()) { if (arg instanceof ValidationHandler) { if (!((ValidationHandler) arg).isValid()) { throw new IllegalArgumentException("参数校验不经过"); } } } } }
自行写方法调用,可以正常执行到aop校验ide
原本写到这里就结束了,偶然发现了一个彩蛋,因而有了升级版。测试
咱们看一下bean validation
的标准注解@javax.validation.constraints.AssertTrue
,这个注解要求bean的属性为true,除了放在属性上,也能够放在get/is方法上。通过测试,==能够放在自定义方法上,该方法名需以is
或get
开头==。spa
说到底,咱们就干了一件事:把校验逻辑放进对应的pojo。其实上面的实现都是不必的,既然都用上了,就不重复造轮子了。把自定义接口和切面都去掉,UserVo
能够变成以下:hibernate
@Data public class UserVo { private String username; private Integer age; private List<String> hobby; @AssertTrue public boolean isValid() { return StringUtils.isNotEmpty(username) && age > 0 && age < 100 && !hobby.isEmpty(); } }
service方法就变成了:
@Validated //打开校验开关 @Service public class UserService { // 入参pojo添加@Valid public void addUser(@Valid UserVo userVo) { // 业务逻辑操做 } }
是否是很熟悉呢?校验效果和前面同样,种一棵树最好的时间是十年前,然后是如今!