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

前言

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

思考

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

整理后的思路

  • 实体属性增加验证注解
  • 注解增加各类验证参数
  • 反射获取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’

总结

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

思路决定出路。

### Java 常用注解列表及功能介绍 #### 1. **元数据注解** 这些注解主要用于提供额外的信息,通常不会直接影响程序的行为。 - `@Override` 用于标记方法重写父类或实现接口中的方法。如果被标记的方法并没有覆盖任何方法,则编译器会报错[^1]。 ```java public class Animal { public void speak() {} } public class Dog extends Animal { @Override public void speak() {} // 正确使用@Override } ``` - `@Deprecated` 表示某个元素(如方法、类等)已被弃用,不建议继续使用。IDE 和编译器会对调用该元素发出警告[^2]。 ```java @Deprecated public void oldMethod() { System.out.println("This method is deprecated."); } ``` - `@SuppressWarnings` 抑制特定的编译器警告信息。常用于忽略未使用的变量或相关的警告[^3]。 ```java @SuppressWarnings("unchecked") public void suppressWarningExample() { List list = new ArrayList(); } ``` --- #### 2. **运行时处理注解** 这些注解可以在运行时通过反射机制读取并执行相应逻辑。 - `@FunctionalInterface` 标明这是一个函数式接口,即只包含一个抽象方法的接口。它有助于编译器验证接口定义是否符合函数式接口的要求。 ```java @FunctionalInterface public interface MyFunction { int apply(int a, int b); } ``` - `@Retention` 指定注解的生命周期,可选值有 `SOURCE`(仅存在于源码)、`CLASS`(默认值,存在于字节码但不在运行时可用)和 `RUNTIME`(可通过反射获取)。例如: ```java import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface CustomAnnotation {} ``` - `@Target` 限定注解的应用范围,比如应用于类 (`ElementType.TYPE`) 或方法 (`ElementType.METHOD`) 等。 ```java import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.METHOD) @interface MethodOnly {} ``` --- #### 3. **Spring 框架相关注解** 以下是 Spring 中常见的注解,广用于依赖注入和其他框架特性支持。 - `@Component` 通用组件注解,表明该类是一个 Spring Bean。 ```java @Component public class MyService {} ``` - `@Autowired` 自动装配依赖项到字段、构造函数或方法参数中。 ```java @Autowired private MyRepository repository; ``` - `@Scope` 声明 Bean 的作用域,默认为单例模式 (`singleton`),也可以设置为原模式 (`prototype`) 来每次请求都创建新实例。 ```java @Component @Scope("prototype") // 创建多例对象 public class PrototypeBean {} ``` - `@Configuration` 和 `@Bean` 分别用来配置类和显式注册 Bean 到容器中。 ```java @Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } } ``` --- #### 4. **校验注解** 这类注解通常配合 Hibernate Validator 使用,在输入验证场景下非常有用。 - `@NotNull`, `@Size`, `@Min`, `@Max` 对字段施加约束条件,确保传入的数据满足预期规则。 ```java public class User { @NotNull(message = "Name cannot be null.") private String name; @Size(min = 8, max = 16, message = "Password must between 8 and 16 characters.") private String password; } ``` --- #### 5. **其他常见注解** - `@Test` (JUnit) 标识测试方法,供单元测试工具识别并执行。 ```java @Test public void testAddition() { assertEquals(2 + 2, 4); } ``` - `@Entity` (JPA) 映射实体类到数据库表结构。 ```java @Entity public class Product { @Id private Long id; private String name; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值