300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > [解锁新姿势] 优化参数前置校验

[解锁新姿势] 优化参数前置校验

时间:2022-03-25 01:45:11

相关推荐

[解锁新姿势] 优化参数前置校验

前言

我们通常写接口都会用到@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这些校验注解,感觉有搞头,尝试自定义,最终实现如下效果。

@Datapublic 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 用法,这里就不展开细讲了,可以出门右转看看其他大佬文章。

@Componentpublic 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 参数。

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

最后

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

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

感谢阅读,下次再见。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。