Spring validator-注解验证-自定义注解实现

1.@NotNull,@NotBlank等常用基础原生注解

本篇文章主讲:高级用法:自定义注解.
需要了解使用基础原生注解,请参考:
链接:
Spring validator常用注解验证.
https://blog.csdn.net/weixin_44903702/article/details/110953515.

2.自定义注解.

目标:

实现默认原生注解中没有的效验方式-
比如: 本文实现的;

本文实现效果如下:

  1. 效验参数必须是指定的值;
  2. 指定的值,即可以指定我们代码中的定义的枚举类的值;
  3. 指定的值,也可以我们手动指定固定的值;
  4. 接收string和Integer类型的效验,其他类型,自定义即可;

1.自定义注解 – 类:

package com.hzsmk.common.base.validation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 自定义参数效验-必须为指定值
 *
 * @author weixz
 * @date 2020-12-10  10:29
 */
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValueValidator.class})
public @interface ValidValue {

    //默认错误消息
    String message() default "参数有误!必须为指定值!";

    String[] strValues() default {};

    int[] intValues() default {};

    Class<? extends Enum> enumValue() default Enum.class;
    //注意此字段值,是枚举效验时,在枚举类里实现的方法名-"checkParam"只是默认方法名,可手动设置其他参数覆盖此值,(比如同一个枚举类里同时设定了多值,可以有多套效验规则时)
    String methodName() default "checkParam";

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

    //负载
    Class<? extends Payload>[] payload() default {};

    //指定多个时使用
    @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        ValidValue[] value();
    }
}

2.自定义注解 – 效验器:

package com.hzsmk.common.base.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Method;

/**
 * 自定义参数效验-必须为指定值-效验器
 *
 * @author weixz
 * @date 2020-12-10  10:33
 */

public class EnumValueValidator implements ConstraintValidator<ValidValue, Object> {

    private String[] strValues;
    private int[] intValues;
    private Class<? extends Enum> enumClass;
    private String methodName;

    @Override
    public void initialize(ValidValue constraintAnnotation) {

        enumClass = constraintAnnotation.enumValue();
        if (enumClass != Enum.class){
            methodName = constraintAnnotation.methodName();
            try {
                // 先判断该enum是否实现了toEnum方法
                enumClass.getDeclaredMethod(methodName, Object.class);
            } catch (NoSuchMethodException e){
                throw new IllegalArgumentException("未添加效验规则,请联系管理员!", e);
            }
        }

        strValues = constraintAnnotation.strValues();
        intValues = constraintAnnotation.intValues();
    }

    /**
     * 优先效验枚举参数,若无枚举参数时,再效验手动指定值
     * @param value
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context ) {

        if (enumClass != Enum.class){
            Method declareMethod;
            try {
                declareMethod = enumClass.getDeclaredMethod(methodName, Object.class);
            }catch (NoSuchMethodException e){
                return false;
            }
            try {
                declareMethod.invoke(null, value);
            } catch (Exception e) {
                return false;
            }
            return true;
        }else {
            if(value instanceof String) {
                for (String s:strValues) {
                    if(s.equals(value)){
                        return true;
                    }
                }
            }else if(value instanceof Integer){
                for (Integer s:intValues) {
                    if(s==value){
                        return true;
                    }
                }
            }
            return false;
        }

    }

}

3.枚举-实现方法:

注意:checkParam方法

package com.hzsmk.lstalent.consts;

import com.hzsmk.common.exception.BusinessException;

import java.util.ArrayList;
import java.util.List;

/**
 * 测试枚举
 *
 * @author weixz
 * @date 2020-12-10  10:40
 */
public enum TestTypeEnum {

    TEST1("test1", "类型1"),
    TEST2("test2", "类型2"),
    TEST3("test3", "类型3");

    //补贴名称
    private String name;
    //补贴类型
    private String type;

    TestTypeEnum(String name, String type) {
        this.name = name;
        this.type = type;
    }

    public static List<String> getNames() {
        ArrayList<String> names = new ArrayList<>();
        for (TestTypeEnum typeEnum : TestTypeEnum.values()) {
            names.add(typeEnum.getName());
        }
        return names;
    }

    public static List<String> getTypes() {
        ArrayList<String> types = new ArrayList<>();
        for (TestTypeEnum typeEnum : TestTypeEnum.values()) {
            types.add(typeEnum.getType());
        }
        return types;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    // **********************注意此方法,********重点
    // 注解校验器的默认方法
    public static void checkParam(Object name) {
        List<String> names = TestTypeEnum.getNames();
        //参数不在该枚举name的范围内,返回
        if (!names.contains(String.valueOf(name))){
            throw new BusinessException("参数不在指定范围内");
        }
    }

	//自定义的其他效验方式方法
    public static void checkType(Object type) {
        List<String> types = TestTypeEnum.getTypes();
        //参数不在该枚举的type范围内
        if (!types.contains(String.valueOf(type))){
            throw new BusinessException("参数不在指定范围内");
        }
    }

}

3.使用及代码详解;

1.使用:

@Data
public class TestDto implements Serializable {
    @ValidValue(enumValue = TestTypeEnum.class)
    private String test1;
    
    @ValidValue(strValues = {"张三","李四","王五"})
    private String test2;

    @ValidValue(intValues = {1,5,7})
    private String test3;

	@ValidValue(enumValue = TestTypeEnum.class , methodName = "checkType")
    private String test4;
}

2.代码解析(不太清晰的看这里,或许有帮助):

1.自定义的ValidValue注解类中

我们看的到:在自定义的ValidValue注解类中:
共有7个属性:

	//默认错误消息
    String message() default "参数有误!必须为指定值!";
    String[] strValues() default {};
    int[] intValues() default {};
    Class<? extends Enum> enumValue() default Enum.class;
    //注意此字段值,是枚举效验时,在枚举类里实现的方法名-"checkParam"只是默认方法名,可手动设置其他参数覆盖此值,(比如同一个枚举类里同时设定了多值,可以有多套效验规则时)
    String methodName() default "checkParam";
    //分组
    Class<?>[] groups() default {};
    //负载
    Class<? extends Payload>[] payload() default {};

其中:
我们自定义的有4个:
分别是:

  • strValues : 允许的字符串值
  • intValues : 允许的数字值
  • enumValue: 枚举类的Class
  • methodName: 枚举类中实现效验的方法名
2.自定义的效验器中
1.属性
	private String[] strValues;
    private int[] intValues;
    private Class<? extends Enum> enumClass;
    private String methodName;

是四个属性;后面会参与效验;

2.方法
@Override
    public void initialize(ValidValue constraintAnnotation) {
    XXXXXXXXXXXXXXXX
    }
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context ) {
    XXXXXXXXX
    }

两个方法都是重新的父类方法:
其中:
initialize方法:
是初始化方法,其会加载我们设置的值进效验器中;
isValid方法:
是效验参数的规则逻辑,返回值true为通过,false为不通过;


至于上面提的,枚举类中,checkParam方法,就是在isValid效验方法中使用反射的方法调用的方法,如果没有实现的话,会抛出异常哦;
大家可以根据自己的需求和想法,对代码仅需修改改造,以便于更适用于自己;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值