Java:基于注解对类实例字段进行通用校验

2023-11-03

前言

后台服务处理前端的请求时,会有这样的一种需求,即校验请求中的参数是否符合校验规则。校验参数是否符合的一种方法是,罗列请求参数,基于校验规则一个一个的校验参数,如果存在不符合的,就返回字段值不符合规则的提示,通过就向下执行。这种方法是可以的,但是不通用,因为需要校验请求参数的地方太多了,罗列式的校验参数会显得效率低下,且,字段的校验规则都是大同小异的,这就有重复造轮子的可能,这是不可取的。所以,应该提取字段的通用校验代码,做到只要在接收参数的model中,通过注解在需要校验的字段上定义好字段不通过时的错误提示,再定义好字段的校验器(校验器是可复用的),即可校验model中的参数是否符合校验规则,如果不通过,返回字段对应的错误提示,如果通过,就返回null。接下来,就分别从代码包结构,两个注解,校验器、校验主逻辑和示例等几个方面,介绍对model的字段进行通用校验的代码。

包结构

如下为model字段校验通用代码的包结构,其中,annotation包中的两个注解之一JavaField是标注在字段上的,用于给字段添加校验规则,JavaFileds用于实现可重复添加相同的注解。validator包中的接口Validator是字段校验器抽象接口,用于定义字段的校验规则,impl包中的是实现Validator接口的实现类,是字段的不同校验器。ValidateUtils是基于注解的实现字段校验的主逻辑代码。
在这里插入图片描述

注解

JavaField注解是添加到需要进行字段值校验的字段上的,用于定义字段校验规则,其有两个参数,一是message,为字段校验不通过时的错误提示,二是validator,它的类型是Class,用于存储校验器的Class对象。

JavaField
package com.nursehealth.util.common.ValidateUtils.annotation;

import java.lang.annotation.*;

/**
 * @author wengym
 * @version 1.0
 * @desc Java字段校验注解
 * @date 2022/12/20 11:05
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(JavaFields.class)
public @interface JavaField {
    /**
     * 校验不通过的提示信息
     */
    String message();
    /**
     * 规则校验器
     */
    Class validator();
}

由于字段的校验可能是多重的,如先要判断字段值是否为空,如果不为空,还要判断值是否符合其他规则,而默认情况下,字段上是不能添加重复的注解的,所以需要让注解可重复添加。为了做到注解可重复添加,首先定义JavaFields注解(注解的名称可随意),该注解需要定义value()方法,注意,该方法的名称就是value而不能是其他的,方法的返回值是需要重复添加的注解数组,即JavaField[]。然后在需要重复添加的注解中,添加@Repeatable(JavaFields.class),如此,即可让注解重复添加。

JavaFields
package com.nursehealth.util.common.ValidateUtils.annotation;

import java.lang.annotation.*;

/**
 * @author wengym
 * @version 1.0
 * @desc Java字段校验注解
 * @date 2022/12/27 11:05
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JavaFields {
    JavaField[] value();
}

校验器

校验器中只有一个校验字段的方法,该方法的作用是接收字段值,然后校验字段值是否符合规则,如果符合则返回true,如果不符合则返回false。校验器是可以复用的,即同一类的校验只要定义一个校验器即可,不用重复写,如判空校验器,只要定义了,就可处处调用。

接口
package com.nursehealth.util.common.ValidateUtils.validator;

public interface Validator {
    /**
     * 校验字段值是否符合规则,通过为true,不通过为false
     *
     * @param fieldValue
     *
     * @author wengym
     *
     * @date 2022/12/20 11:08
     *
     * @return java.lang.Boolean
     */
    Boolean validateField(Object fieldValue);
}
接口实现类
EmptyValidator:判空校验器

字段值为null,为空串,或为为null字符串,表示字段为空,返回false,表示不通过,否则返回true,表示通过。

package com.nursehealth.util.common.ValidateUtils.validator.impl;

import com.nursehealth.util.common.ValidateUtils.validator.Validator;

/**
 * @author wengym
 * @version 1.0
 * @desc 空字段校验器
 * @date 2022/12/20 11:12
 */
public class EmptyValidator implements Validator {

    @Override
    public Boolean validateField(Object fieldValue) {
        if (fieldValue == null || "".equals(fieldValue) || "null".equals(String.valueOf(fieldValue).trim())) {
            return false;
        }
        return true;
    }
}
GenderValidator:性别校验器

性别用1表示男,2表示女,性别只能是1和2,性别为1和2时,返回true,表示通过,否则返回false,表示不通过。

package com.nursehealth.util.common.ValidateUtils.validator.impl;

import com.nursehealth.util.common.ValidateUtils.validator.Validator;

/**
 * @author wengym
 * @version 1.0
 * @desc 性别校验器
 * @date 2022/12/27 11:05
 */
public class GenderValidator implements Validator {

    @Override
    public Boolean validateField(Object fieldValue) {
        String gender = (String)fieldValue;
        if ("1".equals(gender) || "2".equals(gender)) {
            return true;
        }
        return false;
    }
}

校验主逻辑

校验主逻辑要做的就是遍历model字段,再取出字段值,取出字段的校验注解(有多个时是JavaFields,单个时是JavaField,需要分开处理),再实例化校验注解的校验器,然后就基于字段值调用校验器的校验字段方法(validateField),返回的结果如果为true,表示校验通过,则向下执行,如果为false,表示校验不通过,则停止向下执行,并返回校验注解的错误提示信息。如果遍历完字段后,还是没有不通过的字段,则返回null,表示model的参数都通过了校验。

package com.nursehealth.util.common.ValidateUtils;

import com.nursehealth.util.common.ValidateUtils.annotation.JavaField;
import com.nursehealth.util.common.ValidateUtils.annotation.JavaFields;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author wengym
 * @version 1.0
 * @desc Java字段检验工具类
 * @date 2022/12/20 11:17
 */
public class ValidateUtils {
    /**
     * 检验model中的字段值是否符合规则
     *
     * @param model
     * @return java.lang.String
     * @author wengym
     * @date 2022/12/20 11:18
     */
    public static <T> String validate(T model) {
        if (model == null) {
            throw new NullPointerException("参数不能为null");
        }
        Class cls = model.getClass();
        Field[] fields = cls.getDeclaredFields();
        String validateResult;
        for (Field field : fields) {
            Annotation[] ans = field.getAnnotations();
            if (ans.length < 1) {
                continue;
            }
            for (Annotation an : ans) {
                if (an instanceof JavaFields) {
                    JavaField[] values = ((JavaFields)an).value();
                    for (JavaField javaField : values) {
                        validateResult = handleJavaField(javaField, model, field);
                        if (validateResult != null) {
                            return validateResult;
                        }
                    }
                }
                if (an instanceof JavaField) {
                    JavaField value = (JavaField)an;
                    validateResult = handleJavaField(value, model, field);
                    if (validateResult != null) {
                        return validateResult;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 处理单个字段校验注解
     *
     * @param javaField
     *
     * @param model
     *
     * @param field
     *
     * @author wengym
     *
     * @date 2022/12/27 11:24
     *
     * @return java.lang.String
     */
    private static <T> String handleJavaField(JavaField javaField, T model, Field field) {
        try {
            // 校验器的Class
            Class validatorCls = javaField.validator();
            // 检验方法
            Method validateMethod = validatorCls.getMethod("validateField", Object.class);
            // 调用方法
            field.setAccessible(true);
            Object fieldValue = field.get(model);
            field.setAccessible(false);
            Object result = validateMethod.invoke(validatorCls.newInstance(), fieldValue);
            // 结果处理
            Boolean isPass = (Boolean) result;
            if (!isPass) {
                // 没有通过,返回错误提示信息
                return javaField.message();
            }
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

示例

model
package com.nursehealth.model.user;

import com.nursehealth.util.common.ValidateUtils.annotation.JavaField;
import com.nursehealth.util.common.ValidateUtils.validator.impl.EmptyValidator;
import com.nursehealth.util.common.ValidateUtils.validator.impl.GenderValidator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author wengym
 * @version 1.0
 * @desc 用户model
 * @date 2022/12/19 15:52
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserModel {
    /**
     * 用户ID
     */
    @JavaField(message = "用户ID不能为空", validator = EmptyValidator.class)
    private String userId;
    
    /**
     * 性别
     */
    @JavaField(message = "性别不能为空", validator = EmptyValidator.class)
    @JavaField(message = "性别只能为男或女", validator = GenderValidator.class)
    private String gender;
}
具体使用

调用ValidateUtils的validate静态方法校验model中的字段值是否符合规则,如果userId为空,则会返回“用户ID不能为空”,如果gender为空,则会返回“性别不能为空”,如果gender为3,则会返回“性别只能为男或女”,如果字段值符合字段校验规则,则返回null,所以可通过判断校验结果是否为null来判断校验是否通过。

public Object validate(UserModel model) {
    String result = ValidateUtils.validate(model);
    if (!CommonUtil.isNullStr(result)) {
        return APIResponse.errorBack(result);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java:基于注解对类实例字段进行通用校验 的相关文章

随机推荐

  • sql手工注入练习拿flag

    sql手工注入练习拿flag 记录一下自己重新开始学习web安全之路 1 找注入点 url 搜索框 登录框 2 找交互点 用单引号判断是否存在交互点 发现回显不正常 说明url存在有交互点 3 判断类型 char类型 利用and 1 1 和
  • hdu 6127 Hard challenge

    Problem acm hdu edu cn showproblem php pid 6127 Meaning 平面上有 n 个不重合的点 任意三点不共线 任意两点所在直线不经原点 每个点有个 value 任意两个点连出的线段的 value
  • Java 输出数组中指定元素的下标

    输出数组指定元素的下标 public static void main String args 定义一个数组 int array new int 123 456 789 321 654 987 int index printArray ar
  • Chisel入门(三)------Chisel的基本语法2

    概述 继续介绍Chisel的基本语法 3 组件 3 1 Chisel中的组件是模块 Chisel中的每个模块都拓展了class 并包含了接口的io字段 接口是由封装为IO 的Bundle所定义的 Bundle包含的字段表示模块的输入输出端口
  • 用Python3实现文本转语音

    本文首次在公众号 零妖阁 上发表 为了方便阅读和分享 我们将在其他平台进行自动同步 由于不同平台的排版格式可能存在差异 为了避免影响阅读体验 建议如有排版问题 可前往公众号查看原文 感谢您的阅读和支持 利用文本朗读库pyttsx3实现文字转
  • Linux环境rpm yum与dnf详解

    目录 一 rpm工具 1 1 简介 1 2 参数介绍 1 3 常用情景命令 二 yum工具 2 1简介 2 2 常用命令 三 dnf工具 3 1简介 3 2常用命令 本篇文章主要从使用的角度来介绍Linux下rpm yum与dnf三种工具
  • 搭建docker+sonarqube并使用scanner、maven分析项目

    一 搭建docker sonarqube 步骤1 进入服务器 创建一个文件夹sonarqube ssh root ip mkdir sonarqube cd sonarqube 可以选用临时或者正式部署方案 临时部署方案 通用部署 dock
  • 厂房效果图制作

    关于厂房 工厂园区规划 及钢结构类项目效果图制作过程中所经常遇到的一些问题 在这里予以汇总整理 并对场景搭建的流程给予简述 三维场景创建 首先需要把CAD平面规划图纸 或是PDF电子版文档 或是手绘稿等弄清楚 场地占地多少亩 有多少厂房 办
  • 1.1 初识基准测试(Benchmark)

    基准测试 Benchmark 是Go语言中用于衡量代码性能的重要工具 通过编写基准测试函数 并使用testing B提供的方法来记录执行时间和其他指标 我们可以准确地评估代码的性能表现 在开发过程中 合理使用基准测试可以帮助我们优化代码 提
  • 面试系列之基础篇

    强引用 软引用 弱引用 虚引用 一般面试官会这样问 你知道Java中对象的引用类型有哪几种吗 分别讲讲这几种之间的区别 强引用 只要强引用还存在 垃圾收集器永远不会回收被引用的对象 软引用 描述一些还有用但是并非必需的对象 将要发生内存溢出
  • 深度学习与人脸识别系列(3)__基于VGGNet的人脸识别系统

    作者 wjmishuai 出处 http blog csdn net wjmishuai article details 50854155 声明 版权所有 转载请联系作者并注明出处 1 引言 本文中介绍的人脸识别系统是基于这两篇论文 Ver
  • java高qps接口设计_高并发后端设计-限流篇

    系统在设计之初就会有一个预估容量 长时间超过系统能承受的TPS QPS阈值 系统可能会被压垮 最终导致整个服务不够用 为了避免这种情况 我们就需要对接口请求进行限流 限流的目的是通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限
  • leetcode刷题:最大子序积

    1 题目 题解 实现上面的过程 class Solution public int maxProduct vector
  • 删除gitlab上的分支

    好久没有更新了 今天记录一下删除gitlab上的分支的操作 登录仓库后 依次点击 project gt home gt Readme gt repository gt branches 会看到所有master和所有分支 点击右侧删除即可 看
  • 剑指Offer(一):二维数组中查找最大值

    题目 这个题目说的是 给你一个二维数组 matrix 和一个目标值 target 你要在数组里找到这个目标值 然后返回它的行 列下标 如果找不到 则返回 1 1 这个数组的每一行都是从左向右递增 每一列都是从上到下递增 和 二维数组的二分搜
  • 首届《产业数字化金铲奖》榜单来袭

    出品 产业家 2021年 产业数字化是绝对的主题 在这个变幻莫测的一年里 越来越多的企业开始寻求数字化转型方向 它们在工业 农业 医疗 教育等等方向都在努力作出自己的数字化尝试 个体寻找数字进化之路 企业构建新的技术底层 产业定义互联网下半
  • 相机的坐标系转换(1)

    前言 常言道 工欲善其事 必先利其器 如欲明其理 必先定其义 后一句是我编的 不过 这句话还是有道理的 如果要想明白相机的坐标系之间的转换 那么必须明白相机有哪几个坐标系 并对它们下一个明确的定义 只有这样 我们接下来的工作才好开展 正文
  • Rose(双机热备)服务配置

    安装步骤省略 下面只讲配置 1 一般会设置两个job iis 及 sql 此次选择的是SqlServer 版本没有要求 大概流程是 两台服务器 都有不同的ip 内网或者外网 然后虚拟一个ip出来 比如 服务器A 192 168 100 1
  • Leetcode每日一题:面试题02.02.kth-node-from-end-of-list-lcci(返回倒数第k个节点)

    思路 典型得快慢指针问题 快指针与慢指针中间隔着k 2个节点 那么同时 快慢指针 当快指针指向链表结尾节点时 慢指针指向倒数第k个节点 struct ListNode int val ListNode next ListNode int x
  • Java:基于注解对类实例字段进行通用校验

    前言 后台服务处理前端的请求时 会有这样的一种需求 即校验请求中的参数是否符合校验规则 校验参数是否符合的一种方法是 罗列请求参数 基于校验规则一个一个的校验参数 如果存在不符合的 就返回字段值不符合规则的提示 通过就向下执行 这种方法是可