前言

我们通常写接口都会用到 @Valid 注解,通过 @NotNull,@NotEmpty 等等来简单校验我们的接口入参。

但是有些入参,需要查询数据库,这时候 @Valid 自带的校验注解,就满足不了需求了。

所以我们来看看 一些前置校验 优化的“骚操作”

废话不多说,上代码。

场景

我们常常在 Service 层会做一些前置条件的判断,如判断这个用户是否存在,如下代码所示:

public void create(UserCreateDTO dto) {// ----> 前置判断 User existUsername = userRepository.findByName(dto.getName());Fire.checkNotNull(existUsername, GlobalErrorCode.USER_NAME_IS_EXIST);// <----//...省略其他User user = new User();user.setName(dto.getName());user.setEmail(dto.getEmail());userRepository.save(user);
}

这里前置简单校验了 Username 是否存在,其中 Fire 是异常封装类,如果 existUsername 为空,则抛出异常。

简单优化

public void create(UserCreateDTO dto) {// ----> 前置判断 usernameValidate.validate(dto.getName());// <----//...省略其他User user = new User();user.setName(dto.getName());user.setEmail(dto.getEmail());userRepository.save(user);
}@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UsernameValidate {private final UserRepository userRepository;public void validate(String name) {User existUsername = userRepository.findByName(name);Fire.checkNotNull(existUsername, GlobalErrorCode.USER_NAME_IS_EXIST);}
}

这种前置校验,当然也可以用简单抽取 UsernameValidate,进行封装处理。

但是这样也避免不了,手动调用 validate 进行校验。

@Valid优化

思考着,能不能通过注解,解决这种手动调用问题。

于是乎看见了,@NotNull 这些校验注解,感觉有搞头,尝试自定义,最终实现如下效果。

@Data
public class UserCreateDTO {// 增加自定义校验注解@CustomValid(NameRepeatValidator.class)@NotNull(message = "名称不能为空")@ApiModelProperty(value = "名称", required = true)private String name;...省略其他参数
}@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class NameRepeatValidator implements IValidator<String> {private final UserRepository userRepository;@Overridepublic void valid(String arg) {User existUser = userRepository.findByName(arg);Fire.checkNotNull(existUser, GlobalErrorCode.USER_NAME_IS_EXIST);}
}public void create(UserCreateDTO dto) {// 移除// User existUser = userRepository.findByName(dto.getName());// Fire.checkNotNull(existUser, GlobalErrorCode.USER_NAME_IS_EXIST);// 专注业务逻辑User user = new User();user.setPassword(PasswordEncoder.encode(dto.getPassword()));user.setName(dto.getName());user.setEmail(dto.getEmail());userRepository.save(user);
}

只需在 校验的入参,增加 @CustomValid注解,指定对应 NameRepeatValidator 校验类,即可校验 name 是否存在。

NameRepeatValidator 类的 valid 方法,相信大家都看得懂,主要核心的是 IValidator 接口,接着往下看

当然别忘了在接口参数上 增加 @Validated 注解,开启校验.

原理

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
//                                              vvv 指定对应校验器
@Constraint(validatedBy = CustomValidator.class)
public @interface CustomValid {// 额外增加参数Class<? extends IValidator<?>> value();String message() default "";  // 默认需要Class<?>[] groups() default {};  // 默认需要Class<? extends Payload>[] payload() default {}; // 默认需要
}@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CustomValidator implements ConstraintValidator<CustomValid, Object> {private CustomValid customValid;private final CustomValidatorStore customValidatorStore;@Overridepublic void initialize(CustomValid constraintAnnotation) {this.customValid = constraintAnnotation;}@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {if (customValid != null) {customValidatorStore.valid(value, customValid.value());}return true;}
}

这里实现一个 CustomValidator自定义 @Valid 校验。

@CustomValid 注解实现,参考 @NotNull 实现

  • 指定 CustomValidator 为校验器

  • 增加多一个 value 参数,后续获取自定义前置校验器使用

CustomValidator 校验器需实现 ConstraintValidator 接口,指定自定义注解,以及校验参数类型

实现类还需实现 initializeisValid 方法

  • initialize:看着方法名,就知道是来初始化的~
  • isValid:这里主要做参数校验逻辑。CustomValidatorStore 实现如下代码所示:

更多 自定义 @Valid 用法,这里就不展开细讲了,可以出门右转看看其他大佬文章。

@Component
public class CustomValidatorStore {private final Map<Class<?>, IValidator<?>> validatorMap;@Autowiredpublic CustomValidatorStore(List<IValidator<?>> validators) {validatorMap = new HashMap<>();validators.forEach(validator -> validatorMap.put(validator.getClass(), validator));}@SuppressWarnings("unchecked")public <V> void valid(Object object, Class<?> clazz) {IValidator<V> validator = (IValidator<V>) validatorMap.get(clazz);validator.valid((V) object);}
}

CustomValidatorStore 主要是管理 前置校验器,通过 Spring 构造函数注入对应 IValidator实现类,然后根据 Class 转化成对应的 Map,便于后续 valid 处理。

总结

至此,通过一个 @CustomValid 就可以愉快校验入参了,这样 Service 层就可以专注于业务逻辑,无需关注入参。

但是这场景相对单一,同时也会引入新的问题:

  • 如果出现两个参数同时校验。

  • 如果返回校验参数值,如下代码,校验完 username 不存在重复,可能后续业务逻辑,需要这个 existUser 参数。

User existUser = userRepository.findByName(arg);
// 可能后续业务逻辑,需要这个 existUser 参数。

在这里埋下坑,后续想到好的方法在分享,或者有想法的小伙伴可以分享你的方法~

最后

以上就是全部内容,希望能帮助到你~

如有不妥,欢迎指出,大家一起交流学习。

感谢阅读,下次再见。

[解锁新姿势] 优化参数前置校验相关推荐

  1. [解锁新姿势] 兄dei 我感觉你在写bug

    前言: 继上篇 [解锁新姿势] 兄dei,你代码需要优化了 介绍一些代码的优化的小技巧. 但是我们除了在代码编写上需要优雅, 还需要编写对应的测试用例, 以此来保证代码的质量. 在这篇我们继续在学习如 ...

  2. [解锁新姿势] 回想起被 `if-else` 支配的恐惧,我们要打倒 if - else

    前言 [解锁新姿势] 兄dei,你代码需要优化了 在之前文章说到,简单 if-else,可以使用 卫语句 进行优化.但是在实际开发中,往往不是简单 if-else 结构,我们通常会不经意间写下如下代码 ...

  3. android解锁win,Win10电脑解锁新姿势:WP/安卓手机、微软手环当钥匙

    IT之家讯 微软在官方网站公布了Win10的开发路线图,其中描述了目前已经实现的功能.正在预览测试以及正在开发中的功能.根据描述,微软正在开发一种全新的Win10电脑解锁方式. 首先,你可以使用自己的 ...

  4. [解锁新姿势] 兄dei,你代码需要优化了

    黑客(程序员)也是创作者,与画家.建筑师.作家一样. --<黑客与画家> 前言 在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题.甚至有些复制粘贴过来,不加 ...

  5. [解锁新姿势] 兄dei,你代码需要优化了

    前言 在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题.甚至有些复制粘贴过来,不加以整理规范.往往导致项目后期难以维护,更别说后续接手项目的人.所以啊,我们要编写出优雅 ...

  6. [解锁新姿势] 分享 7 个优化代码的技巧

    点击上方 IT牧场 ,选择 置顶或者星标 技术干货每日送达 来源:https://juejin.im/post/6844903983744548877 前言 在我们平常开发过程中,由于项目时间紧张,代 ...

  7. 解锁新姿势:探讨复杂的 if-else 语句“优雅处理”的思路

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:hyzhan43 juejin.im/post/5def65 ...

  8. 巧用『责任链模式』来优化 参数多重校验,非常优雅!

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | ...

  9. windows编程 识别拖动_Quicker 解锁新姿势!Windows 还能这么用?

    不用记住软件复杂的快捷方式,轻轻按下鼠标滚轮,便可唤出当前软件的专属工具箱,一键启动原本要多次点击鼠标的操作,这样的时刻是否很美妙? Excel 选中表格数据加黑框并横向打印,一键搞定 Quicker ...

最新文章

  1. WordPress插件开发: 文章同步到OSC博客插件(OscPress) (四)
  2. Java实现上传文件到指定服务器指定目录
  3. 激战服务器位置,《激战2》世界之战指南(选择服务器)
  4. mybatis入门(五)之Java API
  5. insert 多条数据 并且具有唯一标识符
  6. 工作117:eachat图
  7. Docker 方式 部署 vue 项目 (docker + vue + nginx)
  8. python写tcp服务器_用Python实现一个简单的多线程TCP服务器的教程
  9. 知识图谱-远程监督关系提取
  10. 【Code Review】赛后专访
  11. 运维自动导出业务容器Java堆栈错误日志脚本
  12. Ubuntu安装、更新显卡驱动
  13. web前端简历怎么写?
  14. 如何使用Arduino开发板读/写SD卡模块的数据
  15. css 长单词不换行溢出容器的解决方法 word-wrap与word-break
  16. 反垃圾邮件网关MailCleaner安装与配置1
  17. 计算机设备损坏赔偿制度,南昌工学院实验设备损坏丢失赔偿办法
  18. 网盘(结合百度网盘/阿里网盘)
  19. AltiumDesigner生成彩色电路图PDF
  20. 小马识途营销顾问解读百度官网认证的价值和意义

热门文章

  1. 如何让你的计算机无线网卡和有线网卡同时使用
  2. 大一python基础编程题水果_基本编程题 --python
  3. 《游戏设计艺术(第二版)》第六章个人学习
  4. 【牛客·华为真题】Python扑克牌大小
  5. EasyExcel导出Excel成zip格式怎么解决
  6. ajax页面无刷新上传文件
  7. java毕业设计网上购物商城系统研发(附源码、数据库)
  8. 航班信息数据获取(传入起始点、终点以及日期)
  9. DAO 中存在的不足和优化方案
  10. Python matplotlib作图实例,画一张想要的图片