✨这里是第七人格的博客。小七,欢迎您的到来~✨
🍅系列专栏:【工作小札】🍅
✈️本篇内容: 自定义注解校验器✈️
🍱本篇收录完整代码地址:见文章内容🍱
1 楔子
最近小七在做接口对接的时候,发现某些接口的同一个字段,可能需要做相同的校验。比如,在用户的注册和更新接口都需要判断用户的证件类型是否合法,许多小伙伴们的做法就是直接在代码里写if-else,这种在屎山拉屎的行为,小七实在是难以接收,想着看看能不能在上面雕雕花。
2 偷懒思路
2.1 思路一:工具类
多次复用的代码,我们很自然的想到抽象提取它。于是我们可以提供一个工具类,里面包含证件类型是否合法的校验,需要校验的时候,调用这个方法就好了。
好处:灵活,解耦
缺点:只解决的证件类型这一个参数的校验,如果多了其他类型,还得现实新的方法,写新的if-else。
2.2 思路二:切面
因为是校验入参,我们想一想能不能把这个校验放在Controller呢?把需要校验的接口,都放在切面里,在切面里写逻辑。
好处:入参校验逻辑前移,后面的代码可以更加专注业务逻辑
缺点:不太灵活,如果校验复杂,切面的逻辑将会很重
2.3 思路三:注解
结合上面两种思路,再进一步思考。如果我们可以像使用***@NotBlank***一样,也实现一个注解,这个注解只要传入我们的工具类,就能进行参数的校验,并且错误信息还可以定制化,那就完美了~
查看javax.validation的源码,我们可以发现一个神奇的接口,正好可以实现我们的这个需求,接口如下:
ConstraintValidator
3 代码实现
3.1 编写注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>字典校验注解</p>
*
* @author 第七人格
* @date 2022/12/09
*/
@Target({
ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DictionariesValidator.class)
public @interface DictionariesRef {
/**
* 引用的字典对象,该对象必须包含isValid的静态方法。
*
* @return 校验目标类。
*/
Class<?> constDictClass();
/**
* 错误消息提示。
*
* @return 错误提示。
*/
String message() default "无效的字典引用值!";
/**
* 验证分组。
*
* @return 验证分组。
*/
Class<?>[] groups() default {
};
/**
* 载荷对象类型。
*
* @return 载荷对象。
*/
Class<? extends Payload>[] payload() default {
};
}
3.2 编写实现类
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import *.DictionariesRef;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Method;
/**
* <p>字典验证器-实现类</p>
*
* @author 第七人格
* @date 2022/12/09
*/
public class DictionariesValidator implements ConstraintValidator<DictionariesRef, Object> {
/**
* 注解
*/
private DictionariesRef dictionariesRef;
/**
* 重写初始化方法
*
* @param dictionariesRef 字典校验器注解
*/
@Override
public void initialize(DictionariesRef dictionariesRef) {
this.dictionariesRef = dictionariesRef;
}
/**
* 重写校验方法
*
* true 表示校验通过
* false 表示校验不通过
*
* @param object 待校验对象
* @param constraintValidatorContext 字典验证器上下文
* @return boolean
*/
@Override
public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
// 校验对象为空,则默认校验通过
if (ObjectUtil.isEmpty(object)) {
return true;
}
// 通过反射获取方法。划重点,这里也是为什么,待校验对象需要实现isValid方法的原因
Method method =
ReflectUtil.getMethodByName(dictionariesRef.constDictClass(), "isValid");
return ReflectUtil.invokeStatic(method, object);
}
}
4 实战展示
证件类型枚举(待校验类):
/**
* <p>证件类型枚举</p>
*
* @author 第七人格
* @date 2022/12/09
*/
@Getter
public enum CardTypeEnum {
ID_CARD("I000001","身份证"),
;
private String code;
private String desc;
CardTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
/**
* 得到枚举
*
* @param code 代码
* @return {@link CardTypeEnum}
*/
public static CardTypeEnum getEnum(String code) {
for (CardTypeEnum ge : values()) {
if(ge.getCode().equals(code)) {
return ge;}
}
return null;
}
/**
* 得到详情描述
*
* @param code 代码
* @return {@link String}
*/
public static String getDesc(String code) {
for (CardTypeEnum ge : values()) {
if(ge.getCode().equals(code)) {
return ge.getDesc();}
}
return null;
}
/**
* 是有效
*
* true 表示有效
* false 表示无效
*
* @param code 代码
* @return boolean
*/
public static boolean isValid(String code) {
return getEnum(code) != null;
}
}
入参:
/**
* <p>入参</p>
*
* @author 第七人格
* @date 2022/12/09
*/
@Data
public class DemoReq implements Serializable {
/**
* 证件类型
*
* 注解DictionariesRef
* 第一个参数 constDictClass 传入我们的校验枚举类,此枚举实现了isValid方法
* 第二个参数 message 传入我们自定义的错误信息
*/
@NotBlank(message = "证件类型不能为空")
@DictionariesRef(constDictClass = CardTypeEnum.class, message = "证件类型非法!")
private String cardType;
}
Controller:
/**
* <p>控制层demo</p>
*
* @author 第七人格
* @date 2022/12/09
*/
@RestController
@RequestMapping("/demo")
public class DemoController {
@PostMapping(value = "/v1/apply")
public void apply(@Valid @RequestBody DemoReq req){
// todo
}
}
测试结果:
当cardType传入值为I000001时,检验通过,未见异常。
当cardType传入值不为I000001时,检验不通过,抛出异常信息:证件类型非法!
转载:https://blog.csdn.net/a18602320276/article/details/128260194