java 校验参数接收类 当A字段符合条件时,B字段也需符合什么条件

java 校验参数接收类 当A字段符合条件时,B字段也需符合什么条件 或 B字段需满足 某个注解的条件

使用介绍

参数校验注解
当 caseField 被标记的字段 值符合 thenValues 设定的条件时 则进行 whenField 字段的校验
使用场景 如 :
 当 A字段 接收的值满足 thenValues 中的条件时,则 进行校验B字段 接收的值 是否满足  whenFieldValues 中的条件,如不满足,则返回提示信息
eg:
 1 @CaseWhenField(caseField = "param1", thenValues = {"123", "你猜"}, whenField = "param2",
        whenFieldValues = {"吧啦啦能量", "小魔仙", "全身", "吧啦啦能量测"},
        whenFieldValidatoeClass = Size.class,
        checkMap = "{'min':3,'max':5}"),
        message = "当param1等于123或你猜时,param2 需为吧啦啦能量、小魔仙、全身"
!!!校验优先级 首先检测 whenFieldValidatoeClass 值 如有 则 whenFieldValues 不校验
!!!前提条件:caseField 被标记的字段接收值,满足 thenValues 才进行下面的校验
一、whenFieldValidatoeClass:
 如满足则进行whenFieldValidatoeClass 中指定的注解 @Size的校验, 由 checkMap 做 注解set值,如 {'min':3,'max':5}
 则标识 param2 字段的值长度 最小为3 最大为5
 whenFieldValidatoeClass 可不进行校验 默认不校验
 ================================================================================================
 ;@Size 注解也可为 @Pattern 则  checkMap = "{'pattern':RoleEnum2}" Pattern 为一个正则 校验注解 如其中的正则不满足需求 
  1、可自定义枚举类 实现 EnumInterface {@link EnumInterface},则在项目启动时,会扫描 存入;
  ================================================================================================
  使用方法 : 如 自定枚举为 RoleEnum 则  checkMap = "{'pattern':RoleEnum2}" 为 枚举名称 加 枚举中定义的 key 拼接 获取正则表达式进行校验
 二、无whenFieldValidatoeClass 注解校验 以上面eg为例:
 当 param1 接收到的值为 123 或 你猜 时,进行校验 param2 的值是否为 "吧啦啦能量", "小魔仙", "全身", "吧啦啦能量测" ,如不满足则返回message
 另一种情况 :
   当 whenFieldValues 为默认值 时,则会校验 当 param1 接收到的值为 123 或 你猜 时 ,param2 值不能为空

使用案列

@CaseWhenField(caseField = "param1",
        thenValues = {"123", "你猜"},
        whenField = "param2",
        whenFieldValues = {"吧啦啦能量", "小魔仙", "全身", "吧啦啦能量测",""},
        whenFieldValidatoeClass = FixLength.class,
        message = "当param1等于123或你猜时,param2 需为吧啦啦能量、小魔仙、全身",
        checkMap = "{'length':[12,13,15]}")
//@CaseWhenField(caseField = "param1", thenValues = {"123", "你猜", "333"}, whenField = "param2", whenFieldValues = {"吧啦啦能量", "小魔仙", "全身变"},
//        message = "当param1等于123或你猜时,param2 需为吧啦啦能量、小魔仙、全身变")
//@CaseWhenField(caseField = "param1", thenValues = {"112323111", "333"}, whenField = "param4", message = "当param1为112323111或333时,param4不能为空")
//@CaseWhenField(caseField = "param1", thenValues = {"112323111", "333"}, whenField = "param5",whenFieldValues = {"1"},message = "当param1为112323111或333时,param5需要为1")
@Data
//@AtLeastOneNotEmpty(fields = {"name", "file"}, message = "name、file至少有一个不能为空")
//@AtLeastOneNotEmpty(fields = {"param1", "param2"}, message = "param1、param2至少有一个不能为空")
//@AtLeastOneNotEmpty(fields = {"param3", "param4"}, message = "param3、param4至少有一个不能为空")
public class MyClass extends BaseProcess {
    @Length(max = 20,min = 10)
    private String name;

    private String file;

    private String param1;

    private String param2;

    private String param3;

    private String param4;

    private String param5;
}

上代码

1、注解类

/**
 * 参数校验注解
 * 当 caseField 被标记的字段 值符合 thenValues 设定的条件时 则进行 whenField 字段的校验
 * 使用场景 如 :
 *  当 A字段 接收的值满足 thenValues 中的条件时,则 进行校验B字段 接收的值 是否满足  whenFieldValues 中的条件,如不满足,则返回提示信息
 * eg:
 *  1 @CaseWhenField(caseField = "param1", thenValues = {"123", "你猜"}, whenField = "param2",
 *         whenFieldValues = {"吧啦啦能量", "小魔仙", "全身", "吧啦啦能量测"},
 *         whenFieldValidatoeClass = Size.class,
 *         checkMap = "{'min':3,'max':5}"),
 *         message = "当param1等于123或你猜时,param2 需为吧啦啦能量、小魔仙、全身"
 *
 * !!!校验优先级 首先检测 whenFieldValidatoeClass 值 如有 则 whenFieldValues 不校验
 *
 * !!!前提条件:caseField 被标记的字段接收值,满足 thenValues 才进行下面的校验
 *
 * 一、whenFieldValidatoeClass:
 *  如满足则进行whenFieldValidatoeClass 中指定的注解 @Size的校验, 由 checkMap 做 注解set值,如 {'min':3,'max':5}
 *  则标识 param2 字段的值长度 最小为3 最大为5
 *
 *  whenFieldValidatoeClass 可不进行校验 默认不校验
 *
 *  ================================================================================================
 *  ;@Size 注解也可为 @Pattern 则  checkMap = "{'pattern':RoleEnum2}" Pattern 为一个正则 校验注解 如其中的正则不满足需求 有两种添加自定义枚举正则的方式
 *
 *   1、可自定义枚举类 实现 EnumInterface {@link EnumInterface},则在项目启动时,会扫描 存入;
 
 *  ================================================================================================
 *   使用方法 : 如 自定枚举为 RoleEnum 则  checkMap = "{'pattern':RoleEnum2}" 为 枚举名称 加 枚举中定义的 key 拼接 获取正则表达式进行校验
 *
 *  二、无whenFieldValidatoeClass 注解校验 以上面eg为例:
 *  当 param1 接收到的值为 123 或 你猜 时,进行校验 param2 的值是否为 "吧啦啦能量", "小魔仙", "全身", "吧啦啦能量测" ,如不满足则返回message
 *  另一种情况 :
 *    当 whenFieldValues 为默认值 时,则会校验 当 param1 接收到的值为 123 或 你猜 时 ,param2 值不能为空
 *
 * @author: chenjiaxiang
 * @create: 2023/4/10 09:30
 **/
@Target({ ElementType.TYPE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {CaseWhenFieldValidator.class})
@Repeatable(CaseWhenField.CaseWhenFields.class)
public @interface CaseWhenField {

    /**
     * 字段名 A字段
     */
    String caseField();

    /**
     * A字段的值
     */
    String[] thenValues() default "";

    /**
     * 要校验的字段名
     */
    String whenField();

    /**
     * 要校验的字段值
     */
    String[] whenFieldValues() default "";

    /**
     * 要检验字段使用的注解
     */
    Class<? extends Annotation> whenFieldValidatoeClass() default Null.class;
    /**
     * 校验失败时的提示信息
     */
    String message() default "";

    Class<?>[] groups() default {};

    // 约束注解的有效负载
    Class<? extends Payload>[] payload() default {};

    String checkMap() default "{}" ;


    /**
     * 支持同类多使用
     */
    @Target({ElementType.TYPE})
    @Retention(RUNTIME)
    @Documented
    @interface CaseWhenFields {
        CaseWhenField[] value();
    }
}

2、注解实现方法类

/**
 * @author chenjiaxiang
 * @create 2023/4/10 09:30
 **/

@Slf4j
@Component
public class CaseWhenFieldValidator extends AbstractFactoryMy implements ConstraintValidator<CaseWhenField, Object>{
    private CaseWhenField caseWhenField;

    @Override
    public void initialize(CaseWhenField caseWhenField) {
        this.caseWhenField = caseWhenField;
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        return validateWithDepends(value, context);
    }

    public boolean validateWithDepends(Object validateObject, ConstraintValidatorContext context) {
        Field dependsOnField;
        Field onField;
        try {
            dependsOnField = validateObject.getClass().getDeclaredField(caseWhenField.whenField());
            onField = validateObject.getClass().getDeclaredField(caseWhenField.caseField());
            ReflectionUtils.makeAccessible(dependsOnField);
            ReflectionUtils.makeAccessible(onField);
            //要检验的字段值
            Object value = dependsOnField.get(validateObject);
            Object onFieldVal = onField.get(validateObject);
            //====================
            List<Object> list = new ArrayList<>(Arrays.asList(caseWhenField.thenValues())).stream().filter(x -> !org.springframework.util.StringUtils.isEmpty(x)).collect(Collectors.toList());
            List<Object> dependsOnFieldValues = new ArrayList<>(Arrays.asList(caseWhenField.whenFieldValues())).stream().filter(x -> !org.springframework.util.StringUtils.isEmpty(x)).collect(Collectors.toList());
            //当指定A字段不为空且指定A字段的值不为指定值时跳过校验
            if (!CollectionUtils.isEmpty(list) && !list.contains(onFieldVal)) {
                return true;
            }
            Class<? extends Annotation> validatoeClass = caseWhenField.whenFieldValidatoeClass();
            //满足A字段不为空且为指定值时,则进行判断是否对B字段进行进行注解校验
            if (!validatoeClass.equals(Null.class)) {
                if (super.checkFieldWithAnnotation(validatoeClass)) {
                    boolean b = checkWithSetClass(context, caseWhenField, dependsOnField, value);
                    log.info("字段:{},进行注解:{}校验,验证结果:{}", dependsOnField.getName(), validatoeClass.getName(), b);
                    return b;
                }
                String message = "注解:" + validatoeClass.getTypeName() + ",不支持在FIELD上操作";
                throw new UnsupportedOperationException(message);
            }
            //当注解指定值为空时,则判定该字段不能为空 ,则提示用户
            if (CollectionUtils.isEmpty(dependsOnFieldValues) && org.springframework.util.StringUtils.isEmpty(value)) {
                return false;
            }
            //当注解指定值不为空时,则判定指定字段的值是否包含,如不包含,则提示用户
            if (!CollectionUtils.isEmpty(dependsOnFieldValues) && !dependsOnFieldValues.contains(value)) {
                return false;
            }
        } catch (UnsupportedOperationException unsupportedOperationException) {
            throw unsupportedOperationException;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
        return true;
    }


    private boolean checkWithSetClass(ConstraintValidatorContext context, CaseWhenField caseWhenField, Field dependsOnField, Object value) {
        Class<? extends Annotation> validatoeClass = caseWhenField.whenFieldValidatoeClass();
        if (!CollectionUtils.isEmpty(this.getCache(validatoeClass))) {
            ConstraintValidator<Annotation, Object> instance = getConstraintValidatorByValidatoeClass(validatoeClass, dependsOnField.getType().getTypeName());
            if (Objects.isNull(instance)){
                log.error("注解:{},未查询到验证注解类",validatoeClass.getTypeName());
                return false;
            }
            Map<String, Field> typeWithFiledList = getTypeWithFiledList(instance);
            Map<String, Object> pram = GSON.fromJson(caseWhenField.checkMap(), new TypeToken<Map<String, Object>>() {
            });
            pram.forEach((k, v) -> {
                Field field = typeWithFiledList.get(k);
                Optional.ofNullable(field).ifPresent(t -> {
                    ReflectionUtils.makeAccessible(t);
                    try {
                        Class<?> type = field.getType();
                        boolean basicType = ClassUtil.isBasicType(type);
                        Object convert;
                        if (basicType) {
                            convert = Convert.convert(type, v);
                        } else if (type.isAssignableFrom(Pattern.class)) {
                            String regexType = String.valueOf(v);
                            convert = getPatternByType(regexType);
                        } else {
                            convert = v;
                        }
                        ReflectionUtils.setField(t, instance, convert);
                    } catch (Exception e) {
                        log.error(e.getMessage(), e);
                    }
                });
            });
            typeWithFiledList.clear();
            return instance.isValid(value, context);
        }
        return false;
    }


    @PostConstruct
    public static void initRegexMaps() {
        StopWatch stopWatch = new StopWatch("initRegexMap");
        stopWatch.start();
        initMap();
        stopWatch.stop();
        log.info("自动注入,初始化枚举map成功,耗时:{}ms", stopWatch.getLastTaskTimeMillis());
    }

    private static void initMap() {
        String thisDef = PatternEnum.class.getPackage().getName();
        SCAN_PACKAGE.add(thisDef);
        String startupClass = System.getProperty("sun.java.command");
        Class<Object> objectClass = ClassUtil.loadClass(startupClass, false);
        SpringBootApplication annotation = objectClass.getAnnotation(SpringBootApplication.class);
        Set<String> collect = Arrays.stream(annotation.scanBasePackages()).filter(startupClass::startsWith).collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(collect)) {
            SCAN_PACKAGE.addAll(collect);
        }
        log.info("当前要扫描的包:{}", SCAN_PACKAGE);
        SCAN_PACKAGE.forEach(path -> {
            Set<Class<?>> classSet = ClassUtil.scanPackageBySuper(path, EnumInterface.class);
            if (CollectionUtils.isEmpty(classSet)) {
                log.info("包:{},未扫描到继承EnumInterface类的枚举", path);
            } else {
                addEnumToRegexMap(classSet);
            }
        });
    }


    /**
     * 外部添加正则方法
     */
    public static Map<String, String> getRegexMap() {
        if (CollectionUtils.isEmpty(REGEX)) {
            initMap();
        }
        return REGEX;
    }

    /**
     * 通过传入的正则类型获取对应的正则表达式,如果未查询到则直接使用当前字符串为正则
     */
    public Object getPatternByType(String type) {
        if (getRegexMap().containsKey(type)) {
            return Pattern.compile(getRegexMap().get(type));
        }
        return Pattern.compile(type);
    }
}

3、注解使用到的类

@Slf4j
public abstract class AbstractFactoryMy implements MyValidatorFactory {

    protected static final Map<Class<? extends Annotation>, List<? extends ConstraintValidatorDescriptor<? extends Annotation>>> CACHE = Maps.newConcurrentMap();
    /**
     * 正则
     */
    protected static final Map<String, String> REGEX = Maps.newConcurrentMap();

    protected static final Set<String> SCAN_PACKAGE = new HashSet<>();

    public static final Gson GSON = new GsonBuilder().registerTypeAdapter(new TypeToken<Map<String, Object>>() {
    }.getType(), new DataTypeAdapter()).create();

    private static final ConstraintValidatorFactory DEFAULT_VALIDATOR_FACTORY = Validation.byDefaultProvider().configure().getDefaultConstraintValidatorFactory();

    /**
     * 添加正则枚举值到RegexMap中
     */
    protected static void addEnumToRegexMap(Set<Class<?>> classSet) {
        classSet.stream().filter(ClassUtil::isEnum).forEach(t -> {
            try {
                Object[] enumConstants = t.getEnumConstants();
                Method getKey = t.getMethod("getKey");
                Method getRegex = t.getMethod("getRegex");
                for (Object enumConstant : enumConstants) {
                    String key = t.getSimpleName() + getKey.invoke(enumConstant);
                    String regex = String.valueOf(getRegex.invoke(enumConstant));
                    log.info("key:{},regex:{}", key, regex);
                    if (!REGEX.containsKey(key)) {
                        REGEX.put(key, StringUtils.isEmpty(regex) ? "*" : regex);
                    }
                }
            } catch (Exception e) {
                log.error("添加枚举{}失败:", t.getName(), e);
            }
        });
    }

    /**
     * 确定当前注解是否可以使用在字段上
     */
    protected boolean checkFieldWithAnnotation(Class<? extends Annotation> validatoeClass) {
        ElementType[] elementTypes = validatoeClass.getAnnotation(Target.class).value();
        for (ElementType elementType : elementTypes) {
            if (elementType.equals(ElementType.FIELD)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 从CACHE中获取对应注解类的ConstraintValidatorDescriptor
     */
    protected List<? extends ConstraintValidatorDescriptor<? extends Annotation>> getCache(Class<? extends Annotation> aClass) {
        return CACHE.computeIfAbsent(aClass, this::findByAnnotation);
    }

    /**
     * 根据注解class查询对应的ConstraintValidatorDescriptor
     */
    @Override
    public List<? extends ConstraintValidatorDescriptor<? extends Annotation>> findByAnnotation(Class<? extends Annotation> aClass) {
        Set<String> pathSet = new HashSet<>();
        pathSet.add(aClass.getTypeName());
        ConstraintHelper constraintHelper = ConstraintHelper.forBuiltinConstraints(pathSet);
        List<? extends ConstraintValidatorDescriptor<? extends Annotation>> allValidatorDescriptors = constraintHelper.getAllValidatorDescriptors(aClass);
        if (!CollectionUtils.isEmpty(allValidatorDescriptors)) {
            log.info("添加注解:{}对应的ConstraintValidatorDescriptor", aClass.getTypeName());
        }
        return allValidatorDescriptors;
    }

    /**
     * 获取ConstraintValidator对应的字段
     */
    protected Map<String, Field> getTypeWithFiledList(ConstraintValidator<Annotation, Object> instance) {
        Map<String, Field> fieldMap = new HashMap<>(16);
        Class<?> tempClass = instance.getClass();
        while (tempClass != null) {
            Field[] declaredFields = tempClass.getDeclaredFields();
            if (!CollectionUtils.isEmpty(Arrays.asList(declaredFields))) {
                Arrays.asList(declaredFields).forEach(t -> fieldMap.put(t.getName(), t));
            }
            tempClass = tempClass.getSuperclass();
        }
        return fieldMap;
    }


    @Override
    public ConstraintValidator<Annotation, Object> getConstraintValidatorByValidatoeClass(Class<? extends Annotation> validatoeClass, String typeName) {
        List<? extends ConstraintValidatorDescriptor<? extends Annotation>> cache = this.getCache(validatoeClass);
        Class<Object> objectClass = ClassUtil.loadClass(typeName);
        return cache.stream()
                .filter(descriptor -> ((Class<?>) descriptor.getValidatedType()).isAssignableFrom(objectClass))
                .findFirst()
                .map(constraintValidatorDescriptor ->
                        (ConstraintValidator<Annotation, Object>) DEFAULT_VALIDATOR_FACTORY.getInstance(constraintValidatorDescriptor.getValidatorClass())
                )
                .orElse(null);
    }
}
public interface MyValidatorFactory {
    /**
     * 根据注解class查询对应的ConstraintValidatorDescriptor
     *
     * @param aClass 注解class类
     * @return 注解对应的ConstraintValidatorDescriptor集合
     */
    List<? extends ConstraintValidatorDescriptor<? extends Annotation>> findByAnnotation(Class<? extends Annotation> aClass);

    /**
     * 根据校验注解获取对应的ConstraintValidator
     *
     * @param validatoeClass 校验注解class
     * @param typeName       当前校验字段的类型  Field.getType().getTypeName()
     * @return 注解对应的实现校验类
     */
    ConstraintValidator<Annotation, Object> getConstraintValidatorByValidatoeClass(Class<? extends Annotation> validatoeClass, String typeName);
}
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Null {

}

4、默认注册到缓存中的正则规则

@Getter
public enum PatternEnum implements EnumInterface {
    /**
    * 邮箱
    */
    EMAIL("1","^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$"),
    /**
    * 身份证
    */
    ID_CARD("2","^\\d{6}((((((19|20)\\d{2})(0[13-9]|1[012])(0[1-9]|[12]\\d|30))|(((19|20)\\d{2})(0[13578]|1[02])31)|((19|20)\\d{2})02(0[1-9]|1\\d|2[0-8])|((((19|20)([13579][26]|[2468][048]|0[48]))|(2000))0229))\\d{3})|((((\\d{2})(0[13-9]|1[012])(0[1-9]|[12]\\d|30))|((\\d{2})(0[13578]|1[02])31)|((\\d{2})02(0[1-9]|1\\d|2[0-8]))|(([13579][26]|[2468][048]|0[048])0229))\\d{2}))(\\d|X|x)$"),

    /**
     * 手机号
     */
    PHONE("3",""),
    ;

    /**
     * map Key
     */
    private final String key;

    /**
     * map value  正则
     */
    private final String regex;

    PatternEnum(String key, String regex) {
        this.key = key;
        this.regex = regex;
    }
    public int getThisOrd(){
        return this.ordinal();
    }
}

5、增加其他规则示例

@Getter
public enum RoleEnum implements EnumInterface {

    /**
    * 检校合作省院管理员
    */
    PROVINCE_JXHZGLY("68", "22323"),

    ;
    private String key;

    private String regex;

    RoleEnum(String key, String regex) {
        this.key = key;
        this.regex = regex;
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C__jx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值