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

news2024/12/26 14:50:58

前言

后台服务处理前端的请求时,会有这样的一种需求,即校验请求中的参数是否符合校验规则。校验参数是否符合的一种方法是,罗列请求参数,基于校验规则一个一个的校验参数,如果存在不符合的,就返回字段值不符合规则的提示,通过就向下执行。这种方法是可以的,但是不通用,因为需要校验请求参数的地方太多了,罗列式的校验参数会显得效率低下,且,字段的校验规则都是大同小异的,这就有重复造轮子的可能,这是不可取的。所以,应该提取字段的通用校验代码,做到只要在接收参数的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,单个时是JavaFields,需要分开处理),再实例化校验注解的校验器,然后就基于字段值调用校验器的校验字段方法(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.personnel;

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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/132539.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第20章 离差

第20章 离差 20.1 马尔可夫定理 一般来说&#xff0c;马尔可夫定理能够粗略估计一个随机变量的值等于一个比它的平均值大得多的值的概率。 例子&#xff1a;IQ的平均值是100。我们可以得到&#xff1a;最多1/3的人IQ可以达到300及以上&#xff0c;因为如果IQ>300的人超过…

基于情感词典、k-NN、Bayes、最大熵、SVM的情感极性分析及对比,含数据集

完整代码下载地址&#xff1a;基于情感词典、k-NN、Bayes、最大熵、SVM的情感极性分析及对比&#xff0c;含数据集 1、预处理 &#xff08;1&#xff09;、特征提取 对应文件&#xff1a;feature_extraction.py 最后结果&#xff1a; X^2值前几名的词语。能看出这些词都是一…

喜报丨武汉无名创新科技有限公司荣获国家“高新技术企业”证书,将助力高校科研与竞赛无人机产业加速发展!

2022年11月03日&#xff0c;高新技术企业认定管理官网公告了湖北省2022年第一批通过认定的高新技术企业名单&#xff0c;武汉无名创新科技有限公司&#xff08;简称“无名创新”&#xff09;榜上有名&#xff0c;证书编号为GR202242000480。“国家高新技术企业”认定是对无名创…

Java 并发编程知识总结【二】

3. 阻塞队列与线程池 3.1 阻塞队列 阻塞&#xff1a;必须要阻塞/不得不阻塞 阻塞队列是一个队列&#xff0c;在数据结构中是先进先出 线程1往阻塞队列里添加元素&#xff0c;线程2从阻塞队列里移除元素。 当队列是空的&#xff0c;从队列中获取元素的操作将会被阻塞 当队列…

OASIS协议标准文档的解读_第二部分

8 CELL REFERENCING 8.1 跟GDSII文件一样, 在OASIS文件中&#xff0c; cells也是用名字来标识的。一个CELL record不仅要包括一个cell的定义&#xff0c;还要包括它的名字。 PLACEMENT record根据cell的名字来指定cell的放置位置。跟GDSII一样&#xff0c;在OASIS中没有匿名的c…

APP应用渗透测试思路

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是APP应用渗透测试思路。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未授权…

vue后台系统管理项目-echarts柱状图实现订单统计

echarts柱状图实现订单统计 主要功能 不同订单状态切换显示不同的柱状图数据&#xff1b;根据条件切换选择年度视图、月度视图&#xff1b;根据条件切换指定年份、指定月份显示当前的数据&#xff1b;根据搜索条件查询查看柱状图数据&#xff1b;柱状图数据导出功能&#xff0c…

C#,核心基础算法——完整全面、简单易用、稳定可靠的统计学常用算法之原理介绍、算法精粹与完整的源代码

1、统计学常用算法 统计分析科学 在“政治算术”阶段出现的统计与数学的结合趋势逐渐发展形成了“统计分析科学”。 十九世纪末&#xff0c;欧洲大学开设的“国情纪要”或“政治算数”等课程名称逐渐消失&#xff0c;代之而起的是“统计分析科学”课程。当时的“统计分析科学”…

MongoDB:安装配置

MongoDB有两个服务器版本 &#xff1a;MongoDB 社区版 和 MongoDB 企业版。此篇主要介绍 MacOS 下 MongoDB 社区版的安装&#xff0c;在 “版本” 下拉列表中&#xff0c;选择要下载的 MongoDB 版本&#xff1b;在平台下拉列表中&#xff0c;选择 MacOS。在包下拉列表中&#x…

mysql一主双从环境搭建--docker-compose

mysql一主双从环境搭建–docker-compose 一、工作目录结构 ├── cluster01 │ ├── msql-master01 │ │ └── volumes │ │ ├── conf │ │ │ └── my.cnf │ │ ├── data │ │ ├── initdb │ │ │ …

基础数学(六)——非线性方程求根的数值解法

文章目录期末考核方式求解的一般步骤二分法求根二分法计算样例二分法的优缺点不动点迭代法全局收敛准则收敛性证明样例局部收敛性收敛阶数的定义迭代法具体例题&#xff08;考试必考&#xff09;牛顿迭代法例题&#xff08;使用牛顿法近似目标解&#xff09;&#xff08;考过&a…

Gem5模拟器,FS模式运行自定义程序(九)

FS模拟和SE模拟最大的区别是&#xff1a;FS模拟会启动Linux操作系统&#xff0c;会模拟系统的所有组件。因此需要给系统配置相应的Linux内核以及磁盘镜像&#xff0c;镜像文件作为Linux系统的文件系统。在FS模拟下&#xff0c;使用gem5自带的python脚本configs/example/fs.py。…

从umati 看德国人如何玩OPCUA的

到目前为止&#xff0c;机器的联网标准缺乏统一的协议和语义标准。比较知名的要数每个的MTConnect。fanuc机床的focas协议。未来的发展方向是OPCUA协议。但是实现这个目标并非一日之功。德国的umati 社区也许给我们一些启发。 为了推进机床行业的数字化进程&#xff0c;VDW&…

从2022看2023年发展趋势

前言 时光荏苒&#xff0c;回望即将过去的2022年&#xff0c;前端领域在这一年中整体还是保持平稳向前&#xff0c;但对整个IT产业链路及互联网行业来说&#xff0c;变化还是很大的&#xff0c;下面我将简单分享一下个人对整个行业领域的一些观点与思考。鄙人才疏学浅&#xf…

Linux | 从头开始理解Linux以及工作日常常用命令

Linux的版本 分很多种&#xff0c;家庭用途版本有&#xff1a;Linux Mint、Ubuntu、OpenSUSE、Fedora、PC-BSD&#xff1b;商业用途版本有&#xff1a;Debian、RHEL、CentOS&#xff1b;还有其他版本&#xff1a;FreeBSD、OpenBSD、Solaris、OpenSolaris等。 目录 1.操作系统…

QT学习 控件(三)输入文本类与快捷键:QPlainText;QKeySequenceEdit

文章目录QPlainTextQKeySequenceEdit前情回顾&#xff1a; QLineEit QTextEdit文本输入类 QPlainTextEdit是一个简略版本的类&#xff0c;它的性能优于QTextEdit, 主要是因为在文本文档中使用QPlainTextDocumentLayout简化文本布局 QPlainText QPlainText和QTextEdit大致功能…

Dijkstra求最短路(堆优化)

给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为非负值。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从1 号点走到 n 号点&#xff0c;则输出 −1。 输入格式 第一行包含整数 n 和 m。 接下来 m 行每行包含三个整…

力扣刷题记录——205.同构字符串、217. 存在重复元素、283. 移动零

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《205.同构字符串、217. 存在重复元素、283. 移动零》。 目…

(十四)大白话磁盘文件中, 40个bit位的数据头以及真实数据是如何存储的?

文章目录 1、前情回顾2、详解40个bit位数据头1、前情回顾 之前我们已经给大家讲到了在磁盘上存储数据的时候, 每一行数据都会有变长字段长度列表,逆序存放这行数据里的变长字段的长度,然后会有NULL值列表,对于允许为NULL的字段都会有一个bit位标识那个字段是否为NULL,也是…

shell第五天作业——函数与数组

题目 一、编写函数&#xff0c;实现打印绿色OK和红色FAILED 二、编写函数&#xff0c;实现判断是否有位置参数&#xff0c;如无参数&#xff0c;提示错误 三、编写函数实现两个数字做为参数&#xff0c;返回最大值 四、编写函数&#xff0c;实现两个整数为参数&#xff0c;…