⾃定义注解实现请求参数枚举类型的校验--注解作⽤于字段上⽬录
续上篇(没有基础的可以先看上篇⽂章):
通过本篇⽂章将带你们学会⾃定义注解,解决⼀些业务的场景。
场景
有个API接⼝,⾥⾯存在着枚举值,要求我们在⼊参时进⾏校验。⽐如参数flag需要满⾜XXEnum[a,b,c]枚举,如果⼀个请求参数传了d,此时我们应该拒绝请求。
常见的解决⽅案
1. 参数定义为String类型,直接通过业务进⾏check校验,写个method即可(优点:针对需要校验枚举相对少时,⼯作量少。缺点:每
新增⼀个枚举都需要加逻辑校验,枚举多时不⽅便)
2.  参数定义为枚举类型,直接通过枚举进⾏接收。(针对空值,错误值会抛异常,需要对异常进⾏捕获处理,最好定义全局异常处理
器)
3. 参数定义为String类型,通过⾃定义注解,将注解加在请求参数的dto上可实现校验。(优点:使⽤只需要加⼀个注解校验就⾏)
常见的三种解决⽅案,本篇⽂章将采取第三种⽅案解决。
在开始之前,先回顾⼀下上篇⽂章讲的实现⾃定义注解的三个步骤,1、定义注解 2、实现注解功能 (关键) 3、使⽤定义注解
下⾯我们开始吧。注:必要资源准备放到最后了,为了不占⽤阅读时间及影响排版
1、定义注解
有两种定义⽅式,同时也有两种实现⽅式。
实现⽅式⼀(推荐):
更多参考官⽹:
定义⼀个注解EnumValid,⾥⾯注解类型变量只有⼀个,就是Class=需要校验的枚举类。(唯⼼提⽰,
只有⼀个变量时,⽤value后⾯可以使⽤时可以省略,即@EnumValid(XXEnum)即可)
Spring Constraints使⽤(需要定义EnumValidator类,在第⼆步实现)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValidator.class})
public @interface EnumValid {
/**
* enum class
*/
Class<?> value() default Class.class;
/
/ 注解类型变量,下⾯⼏个是必须定义的,因为使⽤了@Constraint
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
实现⽅式⼆:
AOP⽅式时使⽤
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
springboot aop@Documented
public @interface EnumValid {
/
**
* enum class
*/
Class value() default Class.class;
// 注解类型变量...
}
2、实现注解功能 (关键)
有两种实现⽅式
⽅式⼀(推荐):
基于SpringBoot的相关依赖validation-api,底层也是AOP(像现在这个需求,注解是标注在字段上⾯的,⽐较推荐使⽤这种⽅式⽐较简单)。
⽅式⼆:
基于AOP,通过切⾯的⽅式实现(⼤部分实现⽅式都是针对⽅法注解、类注解,不太适合这⾥的场景)。
2.1 Spring⾃带注解轻松实现
引⼊SpringBoot依赖start,⾥⾯包含了validation-api的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.13.RELEASE</version>
</dependency>
编写EnumValidator.java,在实现⽅法中isValid⾥⾯实现功能
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class EnumValidator implements ConstraintValidator<EnumValid, String> {
private EnumValid enumValid;
@Override
public void initialize(EnumValid constraintAnnotation) {
}
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {        Class<?> value = enumValid.value();
// 是枚举则进⼊校验
if (value.isEnum()) {
Object[] enumConstants = EnumConstants();
for (Object object : enumConstants) {
// 只要有⼀个匹配,就返回true
if (String().equals(s)) {
return true;
}
}
//todo: 不匹配,可以做点其他,这⾥暂时打印⼀下
System.out.println(s + "是⼀个错误值");
}
return false;
}
}
之后进⼊步骤3,使⽤定义注解
2.2 基于AOP实现⾃定义注解
我的项⽬结构,下⾯我要切的是controller层的⽅法
定义⼀个切⾯EnumValidFieldAspect.java
@Component
@Aspect
public class EnumValidFieldAspect {
@Pointcut("execution(* pers.ller..*.*(..))")
public void pointcut() {
System.out.println("access");
}
@Before("pointcut()")
public void before(JoinPoint point) throws IllegalAccessException {
// 切⼊的⽅法
Method method = ((MethodSignature) Signature()).getMethod();
Annotation[][] parameterAnnotations = ParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
for (int j = 0; j < parameterAnnotations[i].length; j++) {
Annotation ann = parameterAnnotations[i][j];
String anName = ann.annotationType().getSimpleName();
if (anName.startsWith("Valid") || anName.startsWith("Validated")) {
// 存在校验,进⾏校验
Args());
return;
}
}
}
}
private void isValid(Object[] args) throws IllegalAccessException {
for (Object object : args) {
Class<?> aClass = Class();
Field[] fields = DeclaredFields();
// 针对⼀个class 的所有field进⾏校验
for (Field field : fields) {
EnumValid enumValid = Annotation(EnumValid.class);
// 为null则说明该字段不需要校验
if (StringUtils.isEmpty(enumValid)) {
continue;
}
// 获取该字段的值
field.setAccessible(true);
String fieldValue = ((object);
Class<?> value = enumValid.value();
// 是枚举则进⼊校验
if (value.isEnum()) {
Object[] enumConstants = EnumConstants();
boolean sign = false;
for (Object enumConstant : enumConstants) {
// 只要有⼀个匹配,就退出该字段的校验(tip:对于枚举类必须重写toString⽅法)                        if (String().equals(fieldValue)) {
sign = true;
break;
}
}
if (!sign) {
//todo: 不匹配,可以做点其他,这⾥暂时打印⼀下
System.out.println(fieldValue + "是⼀个错误值");
throw new RuntimeException(fieldValue + "is a incorrect value");
}
}
}
}
}
}
之后进⼊步骤3,使⽤定义注解
3、使⽤定义注解
我们已经可以实现插⼊标记,修改ReqDTO,加⼊⾃定义注解@EnumValid(NameTypeEnum.class)
4、测试
请求1-失败
结果1
请求2-成功

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。