优化参数前置校验
前言
我们通常写接口都会用到 @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;
@Override
public 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;
@Override
public void initialize(CustomValid constraintAnnotation) {
this.customValid = constraintAnnotation;
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (customValid != null) {
customValidatorStore.valid(value, customValid.value());
}
return true;
}
}
这里实现一个 CustomValidator
,自定义 @Valid
校验。
@CustomValid
注解实现,参考 @NotNull 实现
-
指定 CustomValidator 为校验器
-
增加多一个
value
参数,后续获取自定义前置校验器使用
CustomValidator 校验器需实现 ConstraintValidator
接口,指定自定义注解
,以及校验参数类型
。
实现类还需实现 initialize
和 isValid
方法
- initialize:看着方法名,就知道是来初始化的~
- isValid:这里主要做参数校验逻辑。CustomValidatorStore 实现如下代码所示:
更多 自定义 @Valid 用法,这里就不展开细讲了,可以出门右转看看其他大佬文章。
@Component
public class CustomValidatorStore {
private final Map<Class<?>, IValidator<?>> validatorMap;
@Autowired
public 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 参数。
在这里埋下坑,后续想到好的方法在分享,或者有想法的小伙伴可以分享你的方法~
最后
如有不妥,欢迎指出,大家一起交流学习。
感谢阅读,下次再见。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfeegfa
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01