注解+反射+泛型,解决数据准确性通用校验

5 篇文章 0 订阅

前言

前后端分离开发,接口开发等,数据准确性校验是必不可少步骤。常规的处理方案是在数据发送前、接收后增加验证逻辑,针对参数各属性一一验证是否规范,并对错误的信息进行相应的逻辑处理。这样我们需要写很多很多的针对性强的校验逻辑。在这些校验逻辑中存在很多通用的校验例如:非空判断、值是否在枚举/数据字典范围内、数据是否满足某正则表达式规则等。

思考

如何能减少验证代码,验证代码如何统一、通用

整理后的思路

  • 实体属性增加验证注解
  • 注解增加各类验证参数
  • 反射获取bean对象及各属性
  • 获取属性上的注解及参数
  • 执行注解参数对应的验证逻辑
  • 返回验证结果

干货

注解类
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateField {
    /**
     * 展示/异常提醒文本
     *
     * @return
     */
    String fieldTxt() default "";

    /**
     * 是否非空
     *
     * @return
     */
    boolean notEmpty() default false;

    /**
     * 正则验证
     *
     * @return
     */
    String regex() default "";

    /**
     * 验证枚举
     *
     * @return
     */
    Class<? extends Enum<?>> enumClass() default DefaultEnum.class;

    /**
     * 自定义业务验证逻辑
     * 必须继承{@link ValidateService}接口
     *
     * @return
     */
    Class<? extends ValidateService> validateClass() default ValidateService.class;
}
枚举默认类
public enum DefaultEnum {
    ;
    private String code;
    private String name;

    DefaultEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
业务验证基类
public interface ValidateService {
    /**
     * 执行验证
     * @param value
     * @return
     */
    boolean validate(Object value);
}
验证类

此类为验证主类。

public class ModelValidate {
    private static ApplicationContext applicationContext;

    /**
     * bean验证
     * @param model
     * @return
     */
    public static String validate(Object model) {
        StringBuilder rtn = new StringBuilder();
        try {
            Field[] fields = model.getClass().getDeclaredFields();
            for (Field field : fields) {
                ValidateField validateField = field.getAnnotation(ValidateField.class);
                if (validateField != null) {
                    //设置可访问,不然拿不到private
                    field.setAccessible(true);
                    Object value = field.get(model);
                    validateEmpty(rtn, field, validateField, value);
                    if (value != null) {
                        //正则表达式验证
                        validateRegex(rtn, field, validateField, value);
                        //枚举验证
                        validateEnum(rtn, field, validateField, value);
                        //业务逻辑验证
                        validateByClass(rtn, field, validateField, value);
                    }
                }
            }
        } catch (Exception e) {
            rtn.append("推送的类型不存在!");
        }
        return rtn.toString();
    }

    /**
     * 非空验证
     *
     * @param rtn
     * @param field
     * @param validateField
     * @param value
     */
    private static void validateEmpty(StringBuilder rtn, Field field, ValidateField validateField, Object value) {
        if (validateField.notEmpty()) {
            if (value == null) {
                rtn.append("字段【" + validateField.fieldTxt() + "/" + field.getName() + "】必填;");
            }
        }
    }

    /**
     * 正则表达式验证
     *
     * @param rtn
     * @param field
     * @param validateField
     * @param value
     */
    private static void validateRegex(StringBuilder rtn, Field field, ValidateField validateField, Object value) {
        //验证正则表达式
        if (validateField.regex() != null && validateField.regex().length() > 0) {
            if (!Pattern.matches(validateField.regex(), value.toString())) {
                rtn.append("字段【" + validateField.fieldTxt() + "/" + field.getName() + "】不满足正则验证规则;");
            }
        }
    }

    /**
     * 枚举验证
     *
     * @param rtn
     * @param field
     * @param validateField
     * @param value
     */
    private static void validateEnum(StringBuilder rtn, Field field, ValidateField validateField, Object value) {
        if (validateField.enumClass() != null && !validateField.enumClass().getSimpleName().equals("DefaultEnum")) {
            //此处为getCode可根据业务情况调整
            Object enumObject = EnumUtils.getByMethodAndParam(validateField.enumClass(), "getCode", value);
            if (enumObject == null) {
                rtn.append("字段【" + validateField.fieldTxt() + "/" + field.getName() + "】不是定义枚举中的值。现在值:" + value.toString() + ",应值见:" + validateField.enumClass().getName() + ";");
            }
        }
    }

    /**
     * 类业务逻辑验证
     * @param rtn
     * @param field
     * @param validateField
     * @param value
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private static void validateByClass(StringBuilder rtn, Field field, ValidateField validateField, Object value){
        try {
            if (validateField.validateClass() != null && !validateField.validateClass().getSimpleName().equals("ValidateService")) {
                String methodName = "validate";
                //上线用
                //找到Spring容器中类对应的bean
                Object object = applicationContext.getBean(validateField.validateClass());
                //执行validate方法
                Method method =validateField.validateClass().getMethod(methodName,new Class[]{Object.class});
                boolean validateResult = (boolean) method.invoke(object,value);
//                测试用
//                Object object=validateField.validateClass().newInstance();
//                Method method =validateField.validateClass().getMethod(methodName,new Class[]{Object.class});
                //执行validate方法
//                boolean validateResult = (boolean) method.invoke(object,value);
                
                if (!validateResult) {
                    rtn.append("字段【" + validateField.fieldTxt() + "/" + field.getName() + "】不符合规则;");
                }
            }
        }catch (Exception e){
            rtn.append("字段【" + validateField.fieldTxt() + "/" + field.getName() + "】不符合规则;");
            e.printStackTrace();
        }
    }

测试

验证实体
public class TestValidateModel implements Serializable {
    /**
     * 测试必填
     */
    @ValidateField(notEmpty = true, fieldTxt = "非空")
    private String emptyTrue;
    @ValidateField(notEmpty = true, fieldTxt = "非空")
    private String emptyFalse;
    /**
     * 测试正则(手机号)
     */
    @ValidateField(notEmpty = true, regex = "^((17[0-9])|(14[0-9])|(13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$", fieldTxt = "正则手机号")
    private String regexTrue;
    @ValidateField(notEmpty = true, regex = "^((17[0-9])|(14[0-9])|(13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$", fieldTxt = "正则手机号")
    private String regexFalse;

    /**
     * 测试枚举
     */
    @ValidateField(notEmpty = true, enumClass = TestValidateEnum.class, fieldTxt = "枚举")
    private String enumTrue;
    @ValidateField(notEmpty = true, enumClass = TestValidateEnum.class, fieldTxt = "枚举")
    private String enumFalse;

    /**
     * 测试枚举
     */
    @ValidateField(notEmpty = true, validateClass = TestValidateService.class, fieldTxt = "自定义验证类")
    private String classTrue;
    @ValidateField(notEmpty = true, validateClass = TestValidateService.class, fieldTxt = "自定义验证类")
    private String classFalse;

    public String getEmptyTrue() {
        return emptyTrue;
    }

    public void setEmptyTrue(String emptyTrue) {
        this.emptyTrue = emptyTrue;
    }

    public String getEmptyFalse() {
        return emptyFalse;
    }

    public void setEmptyFalse(String emptyFalse) {
        this.emptyFalse = emptyFalse;
    }

    public String getRegexTrue() {
        return regexTrue;
    }

    public void setRegexTrue(String regexTrue) {
        this.regexTrue = regexTrue;
    }

    public String getRegexFalse() {
        return regexFalse;
    }

    public void setRegexFalse(String regexFalse) {
        this.regexFalse = regexFalse;
    }

    public String getEnumTrue() {
        return enumTrue;
    }

    public void setEnumTrue(String enumTrue) {
        this.enumTrue = enumTrue;
    }

    public String getEnumFalse() {
        return enumFalse;
    }

    public void setEnumFalse(String enumFalse) {
        this.enumFalse = enumFalse;
    }

    public String getClassTrue() {
        return classTrue;
    }

    public void setClassTrue(String classTrue) {
        this.classTrue = classTrue;
    }

    public String getClassFalse() {
        return classFalse;
    }

    public void setClassFalse(String classFalse) {
        this.classFalse = classFalse;
    }
}
测试枚举
public enum TestValidateEnum {
    A("a", "A");
    private String code;
    private String name;

    TestValidateEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
测试验证业务逻辑
public class TestValidateService implements ValidateService {
    @Override
    public boolean validate(Object value) {
        if(value.toString().equals("1")){
            return true;
        }
        return false;
    }
}
测试类
public class TestModelValidate {
    public static void main(String[] args) {
        TestValidateModel model = new TestValidateModel();
        model.setEmptyTrue("AAA");
        model.setRegexTrue("18888888888");
        model.setRegexFalse("88888888888");
        model.setEnumTrue("a");
        model.setEnumFalse("b");
        model.setClassTrue("1");
        model.setClassFalse("0");
        String rtn = ModelValidate.validate(model);
        System.out.println(rtn);
    }
}
测试结果

Connected to the target VM, address: ‘127.0.0.1:0’, transport: ‘socket’
字段【非空/emptyFalse】必填;字段【正则手机号/regexFalse】不满足正则验证规则;字段【枚举/enumFalse】不是定义枚举中的值。现在值:b,应值见:cn.pminfo.standard.push.common.test.TestValidateEnum;字段【自定义验证类/classFalse】不符合规则;
Disconnected from the target VM, address: ‘127.0.0.1:0’, transport: ‘socket’

总结

此通用类使用后,使用注解的方式在实体属性上添加验证规则,即可实现数据准确校验。此时有一个考虑,我们是否可以扩展,增加拦截器,拦截系统情况,根据请求的类型判断是否调用该验证方法。所有接口入口即可不用关心参数是否准确。

思路决定出路。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值