springboot注解–进阶–01–基于注解实现对实体类字段校验
1、需求
- 期望在字段上加上注解,实现对字段的校验
- 开发人员能 主动 校验
2、思路
- 获取字段值A
- 获取字段注解B
- 比较A和B的关系,看A是否符合B
3、代码
我这里只写了一个notNull注解,开发人员可以继续扩展。
3.1、代码结构
3.2、core
Restrain
package com.hd.business.business.common.validator.core;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
/**
* 约束注解
* 所有自定义的注解都要引入 约束注解
* 案例:
* @Target({ElementType.FIELD})
* @Retention(RetentionPolicy.RUNTIME)
* @Documented
* @Restrain( validatedBy = {NotNullValidator.class})
* public @interface NotNull {}
*
*/
public @interface Restrain {
/**
* 约束的具体实现类class
* @return
*/
Class<? extends RestrainValidator<?, ?>>[] validatedBy();
}
RestrainValidator
package com.hd.business.business.common.validator.core;
import java.lang.annotation.Annotation;
/**
* 约束验证器
* 所有自定义的注解都要 实现 约束验证器
* 举例: 非空注解 的 约束验证器
* public class NotNullValidator extends RestrainValidator<NotNull, Object>
*
* @param <A> : 要被约束的注解
* @param <T> : 要被约束的字段 值类型
*/
public abstract class RestrainValidator<A extends Annotation, T> {
protected A targetAnnotation;
/**
* 校验的模板
*
* @param constraintAnnotation
* @param value
*/
public final void check(A constraintAnnotation, T value) {
initialize(constraintAnnotation);
if (!isValid(value)) {
//校验不成功,抛异常
String message = message();
throw new ValidatorException(message);
}
}
/**
* 初始化方法
*
* @param constraintAnnotation:要被约束的注解
*/
private void initialize(A constraintAnnotation) {
targetAnnotation = constraintAnnotation;
}
;
/**
* 校验方法
*
* @param value:要被约束的字段值
*/
public abstract boolean isValid(T value);
/**
* isValid=false的提示
*/
public abstract String message();
}
ValidatorException
/**
* @Description 约束验证的异常
* @Author feizhou
* @Date 2023/3/20 18:39
* @Verson 1.0
**/
public class ValidatorException extends RuntimeException {
public ValidatorException() {
}
public ValidatorException(String message) {
super(message);
}
public ValidatorException(String message, Throwable cause) {
super(message, cause);
}
public ValidatorException(Throwable cause) {
super(cause);
}
}
ValidatorTools
package com.hd.business.business.common.validator.core;
import com.hd.business.business.dataRecord.bean.synData.SynDataParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @ClassName: ValidatorTools
* @Description 校验工具
* @Author feizhou
* @Date 2023/3/20 18:39
* @Verson 1.0
**/
public class ValidatorTools {
protected static Logger logger = LoggerFactory.getLogger(ValidatorTools.class);
protected volatile static Map<Class, List<Field>> fieldMap = new HashMap<>();
/**
* 校验 obj 的参数 是否合法
*
* @param obj
*/
public static void isValid(Object obj) {
List<Field> fields = fieldMap.get(obj.getClass());
if (fields == null) {
//获取所有字段
fields = getFields(obj);
fieldMap.put(obj.getClass(), fields);
}
//递归校验每个字段
fields.stream().forEach(e -> isValid(e, obj));
}
/**
* 对单个字段进行校验
*
* @param field
* @return
*/
public static void isValid(Field field, Object obj) {
// 获取字段值
field.setAccessible(true);
Object checkValue = null;
try {
checkValue = field.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//获取所有注解
Annotation[] annotations = field.getAnnotations();
//没有注解,就不处理
if (annotations == null || annotations.length == 0) {
return;
}
//列出 带有Restrain注解的注解
List<Annotation> validatorAnno = Arrays.stream(annotations).filter(e -> e.annotationType().isAnnotationPresent(Restrain.class)).collect(Collectors.toList());
for (Annotation an : validatorAnno) {
//获取注解上的Restrain注解
Restrain annotation = an.annotationType().getAnnotation(Restrain.class);
//获取RestrainValidator实现类的class
Class<? extends RestrainValidator<?, ?>>[] restrainImplClasses = annotation.validatedBy();
//递归 RestrainValidator实现类的class
for (Class<? extends RestrainValidator<?, ?>> restrainImplClass : restrainImplClasses) {
//获取RestrainValidator接口的实例化对象
RestrainValidator restrainImpl = getRestrainImpl(restrainImplClass);
restrainImpl.check(an, checkValue);
}
}
}
/**
* 获取RestrainValidator接口的实例化对象
*
* @param c
* @return
*/
public static RestrainValidator getRestrainImpl(Class<? extends RestrainValidator<?, ?>> c) {
//获取实例化
RestrainValidator obj = null;
try {
obj = c.getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 获取所有字段
*
* @param obj
* @return
*/
public static List<Field> getFields(Object obj) {
Class<?> clazz = obj.getClass();
Field[] currentFields = clazz.getDeclaredFields();
List<Field> list = new ArrayList<>();
for (Field field : currentFields) {
// 排除 序列化字段
if ("serialVersionUID".equalsIgnoreCase(field.getName())) {
continue;
}
// 排除 static 和 final 的编码
int mod = field.getModifiers();
if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
continue;
}
list.add(field);
}
return list;
}
public static void main(String[] args) {
SynDataParam synDataParam1 = new SynDataParam();
synDataParam1.setFieldBeanId("1111");
synDataParam1.setFieldTypeId("11112");
synDataParam1.setFiledTypeName("11112");
isValid(synDataParam1);
System.out.println("成功");
SynDataParam synDataParam2 = new SynDataParam();
synDataParam2.setFieldBeanId("1111");
synDataParam2.setFieldTypeId("11112");
// synDataParam2.setFiledTypeName("11112");
isValid(synDataParam2);
System.out.println("成功2");
}
}
3.2、validator
NotNull
package com.hd.business.business.common.validator;
import com.hd.business.business.common.validator.core.Restrain;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Restrain(
validatedBy = {NotNullValidator.class}
)
/**
* 非空校验注解
*/
public @interface NotNull {
String message() default "该字段非空";
}
NotNullValidator
package com.hd.business.business.common.validator;
import com.hd.business.business.common.tools.StringUtil;
import com.hd.business.business.common.validator.core.RestrainValidator;
/**
* @ClassName: NotNullValidator
* @Description 非空校验实现类型
* @Author feizhou
* @Date 2023/3/20 18:39
* @Verson 1.0
**/
public class NotNullValidator extends RestrainValidator<NotNull, Object> {
@Override
public boolean isValid(Object obj) {
return StringUtil.isNotEmpty(obj);
}
@Override
public String message() {
return targetAnnotation.message();
}
}
3.3、test
package com.hd.business.business.common.validator.test;
import com.hd.business.business.common.validator.NotNull;
import com.hd.business.business.common.validator.core.ValidatorTools;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @ClassName: DataRecordBean
* @Description 请描述下该类是做什么的
* @Author feizhou
* @Date 2023/3/3 11:08
* @Verson 1.0
**/
@Data
@NoArgsConstructor
public class TestBean implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(message = "id 不能为空")
private Long id; // id
private String configCode;
private String configName;
public static void main(String[] args) {
TestBean bean = new TestBean();
bean.setConfigName("111");
bean.setConfigCode("code");
ValidatorTools.isValid(bean);
}
}