This commit is contained in:
Hua
2024-07-20 14:34:56 +08:00
commit d6ea3c61dc
23 changed files with 908 additions and 0 deletions

View File

@ -0,0 +1,13 @@
package com.hua.annotation;
import java.lang.annotation.*;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Handles {
Class<? extends Annotation> value();
}

View File

@ -0,0 +1,42 @@
package com.hua.test.valid;
import com.hua.valid.annotation.ConditionNotNull;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
public class Student {
@ConditionNotNull(test = "teaName == '王老师' and teaAge == 28 and teaSex == 1", message = "老师名字为王老师年龄为18性别为1的时候 name不能为null")
private String name;
private int age;
private int sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}

View File

@ -0,0 +1,56 @@
package com.hua.test.valid;
import com.hua.valid.annotation.ValidPlus;
import javax.validation.Valid;
import java.util.List;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
@ValidPlus
public class Teach {
private String teaName;
private int teaAge;
private int teaSex;
@Valid
private List<Student> listStu;
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public int getTeaAge() {
return teaAge;
}
public void setTeaAge(int teaAge) {
this.teaAge = teaAge;
}
public int getTeaSex() {
return teaSex;
}
public void setTeaSex(int teaSex) {
this.teaSex = teaSex;
}
public List<Student> getListStu() {
return listStu;
}
public void setListStu(List<Student> listStu) {
this.listStu = listStu;
}
}

View File

@ -0,0 +1,49 @@
package com.hua.test.valid;
import com.hua.valid.AnnotationHandleFactory;
import com.hua.valid.ErrorMessage;
import com.hua.valid.HandleDTO;
import java.util.List;
public class Test {
public static void main(String[] args) {
Teach teach = init();
ErrorMessage errorMessage = new ErrorMessage();
if (!AnnotationHandleFactory.handle(new HandleDTO.HandleDTOBuilder()
.setOriginObj(teach)
.setAnnotations(teach.getListStu().get(0).getClass().getDeclaredFields()[0].getAnnotations())
.setField(teach.getListStu().get(0).getClass().getDeclaredFields()[0])
.setFieldValue(teach.getListStu().get(0).getName())
.setErrorMessage(errorMessage)
.build())) {
System.out.println("验证失败: " + errorMessage.getErrorMessage());
} else {
System.out.println("验证成功");
}
}
public static Teach init() {
Student student = new Student();
student.setName("张同学");
student.setAge(18);
student.setSex(1);
Student student1 = new Student();
student1.setName(null);
student1.setAge(19);
student1.setSex(2);
Teach teach = new Teach();
teach.setTeaAge(28);
teach.setTeaName("王老师");
teach.setTeaSex(1);
List<Student> student11 = List.of(student1, student);
teach.setListStu(student11);
return teach;
}
}

View File

@ -0,0 +1,147 @@
package com.hua.valid;
import cn.hutool.core.util.ArrayUtil;
import com.hua.annotation.Handles;
import com.hua.valid.annotation.PackageScanConfig;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Consumer;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
public class AnnotationHandleFactory {
// 缓存已加载的注解处理器
private static final Map<Class<? extends Annotation>, AnnotationHandler> cache = new HashMap<>();
private static boolean useSPI = true; // 是否使用SPI加载处理器
private static boolean useReflection = false; // 是否使用反射加载处理器
private static String[] packagesToScan = {"com.hua.valid"}; // 反射扫描的包路径
// 静态初始化块,在类加载时进行处理器初始化
static {
resolvePackageConfig();
initializeHandlers();
}
/**
* 配置处理器加载方式和扫描路径。
*
* @param useSpi 是否使用SPI加载处理器。
* @param useRef 是否使用反射加载处理器。
* @param packages 反射扫描的包路径。
*/
public static void configure(boolean useSpi, boolean useRef, String[] packages) {
useSPI = useSpi;
useReflection = useRef;
packagesToScan = packages;
cache.clear(); // 清除缓存
initializeHandlers(); // 重新初始化处理器
}
private static void resolvePackageConfig() {
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
.setScanners(new TypeAnnotationsScanner(), new SubTypesScanner()));
// 查找所有应用了PackageScanConfig注解的类
Set<Class<?>> configClasses = reflections.getTypesAnnotatedWith(PackageScanConfig.class);
if (configClasses.isEmpty()) {
return;
}
PackageScanConfig config = configClasses.stream().findFirst().get().getAnnotation(PackageScanConfig.class);
useSPI = config.useSPI();
useReflection = config.useReflection();
packagesToScan = config.packages();
}
// 初始化处理器根据配置决定使用SPI或反射加载
private static void initializeHandlers() {
if (useSPI) {
loadHandlersViaSPI();
}
if (useReflection) {
loadHandlersViaReflection();
}
}
// 通过SPI加载注解处理器
private static void loadHandlersViaSPI() {
ServiceLoader<AnnotationHandler> loadedHandlers = ServiceLoader.load(AnnotationHandler.class);
loadedHandlers.forEach(handler -> {
Handles handles = handler.getClass().getAnnotation(Handles.class);
if (handles != null) {
cache.put(handles.value(), handler);
}
});
}
// 通过反射加载注解处理器
private static void loadHandlersViaReflection() {
// 单独的方法用于加载处理器
Consumer<Reflections> loadHandlers = (reflections) -> {
reflections.getTypesAnnotatedWith(Handles.class).forEach(cls -> {
try {
AnnotationHandler handler = (AnnotationHandler) cls.getDeclaredConstructor().newInstance();
Handles handlesAnnotation = cls.getAnnotation(Handles.class);
cache.put(handlesAnnotation.value(), handler);
} catch (ReflectiveOperationException e) {
// 推荐使用日志记录异常,而非仅仅打印堆栈信息
e.printStackTrace();
}
});
};
if (ArrayUtil.isEmpty(packagesToScan)) {
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
.setScanners(new TypeAnnotationsScanner(), new SubTypesScanner()));
loadHandlers.accept(reflections);
} else {
for (String pkg : packagesToScan) {
Reflections reflections = new Reflections(new ConfigurationBuilder()
.forPackages(pkg)
.setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()));
loadHandlers.accept(reflections);
}
}
}
/**
* 根据注解类型获取对应的处理器。
*
* @param annotation 注解实例。
* @return 对应的注解处理器如果未找到返回null。
*/
public static AnnotationHandler getHandler(Annotation annotation) {
return cache.get(annotation.annotationType());
}
/**
* 处理给定的注解数据对象。
*
* @param handleDTO 包含注解信息的数据传输对象。
* @return 处理结果,成功或失败。
*/
public static boolean handle(HandleDTO handleDTO) {
for (Annotation annotation : handleDTO.getAnnotations()) {
AnnotationHandler handler = getHandler(annotation);
if (handler != null) {
handleDTO.setCurrentAnnotation(annotation);
return handler.handle(handleDTO);
}
}
return true;
}
}

View File

@ -0,0 +1,5 @@
package com.hua.valid;
public interface AnnotationHandler {
boolean handle(HandleDTO handleDTO);
}

View File

@ -0,0 +1,32 @@
package com.hua.valid;
import cn.hutool.core.util.StrUtil;
import com.hua.annotation.Handles;
import com.hua.valid.annotation.ConditionNotNull;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
@Handles(ConditionNotNull.class)
public class ConditionNotNullHandler implements AnnotationHandler {
@Override
public boolean handle(HandleDTO handleDTO) {
ConditionNotNull conditionValidate = (ConditionNotNull) handleDTO.getCurrentAnnotation();
String test = conditionValidate.test();
if (StrUtil.isEmpty(test)) {
return true;
}
ExpressionParser parser = new SpelExpressionParser();
Boolean value = parser.parseExpression(test).getValue(handleDTO.getOriginObj(), Boolean.class);
if (Boolean.TRUE.equals(value) && handleDTO.getFieldValue() == null) {
handleDTO.getErrorMessage().addErrorObj(conditionValidate.message(), handleDTO.getField());
return false;
}
return true;
}
}

View File

@ -0,0 +1,69 @@
package com.hua.valid;
import cn.hutool.core.util.ReflectUtil;
import com.hua.valid.annotation.ValidPlus;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Valid;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
public class ConditionValidateImpl implements ConstraintValidator<ValidPlus, Object> {
@Override
public void initialize(ValidPlus constraintAnnotation) {
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
ErrorMessage errorMessage = new ErrorMessage();
validObj(o, errorMessage, o);
if(errorMessage.isError()) {
constraintValidatorContext.disableDefaultConstraintViolation();
constraintValidatorContext.buildConstraintViolationWithTemplate(errorMessage.getErrorMessage()).addConstraintViolation();
return false;
}
return true;
}
private void validObj(Object o, ErrorMessage errorMessage, Object originObj) {
if (o instanceof List<?> list) {
for (Object obj : list) {
validObj(obj, errorMessage,originObj);
}
return;
}
Field[] fields = ReflectUtil.getFields(o.getClass());
for (Field field : fields) {
Valid annotation = field.getAnnotation(Valid.class);
if (annotation != null) {
validObj(ReflectUtil.getFieldValue(o,field),errorMessage, originObj);
}
Annotation[] annotations = field.getAnnotations();
if (annotations == null || annotations.length == 0) {
continue;
}
Object fieldValue = ReflectUtil.getFieldValue(o, field);
Annotation formatAnnotation = field.getAnnotation(ValidPlus.class);
if (formatAnnotation == null) {
throw new RuntimeException("Format annotation is missing on the field.");
}
HandleDTO handleDTO = new HandleDTO.HandleDTOBuilder().setOriginObj(originObj)
.setAnnotations(annotations)
.setAnnotations(annotations)
.setField(field)
.setErrorMessage(errorMessage)
.setFieldValue(fieldValue).build();
AnnotationHandleFactory.handle(handleDTO);
}
}
}

View File

@ -0,0 +1,57 @@
package com.hua.valid;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
public class ErrorMessage {
private final List<ErrorObj> listErrorObj = new ArrayList<>();
public void addErrorObj(String errorMessage, Field errorField){
ErrorObj errorObj = new ErrorObj(errorMessage, errorField);
listErrorObj.add(errorObj);
}
public boolean isError() {
return listErrorObj.size() > 0;
}
public String getErrorMessage(){
return listErrorObj.stream().map(ErrorObj::getErrorMessage).collect(Collectors.joining(","));
}
public static class ErrorObj{
private String errorMessage;
private Field errorField;
public ErrorObj(String errorMessage, Field errorField) {
this.errorMessage = errorMessage;
this.errorField = errorField;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public Field getErrorField() {
return errorField;
}
public void setErrorField(Field errorField) {
this.errorField = errorField;
}
}
}

View File

@ -0,0 +1,128 @@
package com.hua.valid;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
public class HandleDTO {
/**
* 要验证的实体类的原始值(有可能 实体类里面嵌套实体的情况)
*/
private Object originObj;
/**
* 属性上加的所有的注解
*/
private Annotation[] annotations;
/**
* 当前要处理的注解
*/
private Annotation currentAnnotation;
/**
* 加注解的那个字段
*/
private Field field;
/**
* 错误信息
*/
private ErrorMessage errorMessage;
/**
* 加注解的那个字段值
*/
private Object fieldValue;
public Object getOriginObj() {
return originObj;
}
public void setOriginObj(Object originObj) {
this.originObj = originObj;
}
public Annotation[] getAnnotations() {
return annotations;
}
public void setAnnotations(Annotation[] annotations) {
this.annotations = annotations;
}
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
public ErrorMessage getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(ErrorMessage errorMessage) {
this.errorMessage = errorMessage;
}
public Object getFieldValue() {
return fieldValue;
}
public void setFieldValue(Object fieldValue) {
this.fieldValue = fieldValue;
}
public Annotation getCurrentAnnotation() {
return currentAnnotation;
}
public void setCurrentAnnotation(Annotation currentAnnotation) {
this.currentAnnotation = currentAnnotation;
}
public static class HandleDTOBuilder {
private final HandleDTO handleDTO;
public HandleDTOBuilder() {
handleDTO = new HandleDTO();
}
public HandleDTOBuilder setOriginObj(Object originObj) {
handleDTO.setOriginObj(originObj);
return this;
}
public HandleDTOBuilder setAnnotations(Annotation[] annotations) {
handleDTO.setAnnotations(annotations);
return this;
}
public HandleDTOBuilder setField(Field field) {
handleDTO.setField(field);
return this;
}
public HandleDTOBuilder setErrorMessage(ErrorMessage errorMessage) {
handleDTO.setErrorMessage(errorMessage);
return this;
}
public HandleDTOBuilder setFieldValue(Object fieldValue) {
handleDTO.setFieldValue(fieldValue);
return this;
}
public HandleDTOBuilder setCurrentAnnotation(Annotation currentAnnotation) {
handleDTO.setCurrentAnnotation(currentAnnotation);
return this;
}
public HandleDTO build() {
return handleDTO;
}
}
}

View File

@ -0,0 +1,16 @@
package com.hua.valid.annotation;
import java.lang.annotation.*;
/**
* @author Hua
* @since 2024/7/18 下午2:42
*/
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ConditionNotNull {
String test() default "";
String message() default "";
}

View File

@ -0,0 +1,14 @@
package com.hua.valid.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE) // 类或接口上使用
@Retention(RetentionPolicy.RUNTIME) // 在运行时可见
public @interface PackageScanConfig {
boolean useSPI() default false; // 默认为空数组
boolean useReflection() default false; // 默认为空数组
String[] packages() default {}; // 默认为空数组
}

View File

@ -0,0 +1,21 @@
package com.hua.valid.annotation;
import com.hua.valid.ConditionValidateImpl;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @author Hua
* @since 2024/7/18 下午2:43
*/
@Documented
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ConditionValidateImpl.class)
public @interface ValidPlus {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1 @@
com.hua.valid.ConditionNotNullHandler