【java】实现自定义注解校验——方法一
自定义注解校验的实现步骤:
1.创建注解类,编写校验注解,即类似@NotEmpty注解
2.编写自定义校验的逻辑实体类,编写具体的校验逻辑。(这个类可以实现ConstraintValidator这个接口,让注解用来校验)
3.开启使用自定义注解进行校验。
第一种实现自定义注解的方式:
一、创建注解类:
1、创建类时,选择Annotation类型

2、编写注解类
编写注解类时,需要用到元注解来规定注解的实现方式等;
package cn.hsa.bis.api.common.utils;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Description 基础校验注解* @Author chenzz* @create 2020/3/6 19:43*/
//Target注解是指定当前自定义注解可以使用在哪些地方,这里仅仅让他可以使用在字段上;
@Target(ElementType.FIELD)
//指定当前注解保留到运行时;
@Retention(RetentionPolicy.RUNTIME)
public @interface ValiInfo {/*** 最小长度*/int minLen() default 0;/*** 最大长度*/int maxLen() default 2147483647;/*** 非空校验*/boolean notBlank() default false;/*** 字典校验*/String codeType() default "";/*** 非法字符校验* 特殊字符:ascii码表中除字母、数字外的所有字符,顿号(、),间隔号(·)*/String illegalCharRegExp() default ".*[\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7f\\u3001\\u00b7]+.*";/*** 正则校验*/String regexp() default "";/*** 字段含义*/String mean() default "";/*** 汉字校验*/String regexpChinese() default "";
}
3、元注解 (编写注解类时,必须要明白的)
刚创建出来的注解是不能使用的。因为我们不知道注解应该加在什么地方,在什么时间生效,所以就引出来元注解的概念。元注解就是注解的注解(有点绕口令的感觉)。专门用来注解注解类型。
java的元注解:@Target,@Retention,@Documented,@Inherited
@Target直接指明了该注解生效的位置,该参数没有default值,必填。参数是一个枚举类型

@Retention指明了注解生效的阶段

@Documented
当我们用javaDoc生成API文档时,是否将该注解记录到API文档中。
@Inherited
这个注解是说,我们的注解是否需要被子类继承。是发生在子类和父类之间的一种注解。
注意:当我们的注解作用域是Element.TYPE时,我们定义在类上的注解可以被子类继承。但是如果我们注解的作用域是Element.METHOD时,并且父类的该方法被子类重写,那作用在父类的注解不会被子类继承。所以如果我们在接口的方法中定义的注解,永远不会被实现类继承,因为实现类一定会重写接口中的方法。
二、自定义注解校验逻辑的实现:
这里有两种实现方式,
一种是当注解仅仅作用在字段(属性)上生效时:可以在工具类中编写方法进行逻辑校验;
另一种是当注解作用在方法上生效时,可以使用@Constraint注解,指明了校验类,进行校验;
这里只实现第一种。
第一种:当自定义注解仅仅作用在字段上生效时,在工具类中编写方法进行逻辑校验(在工作中用的比较多)
public class ToolUtils {/*** 对象基础格式校验,针对带有valiInfo注解的属性* @param obj* @param ignoreFields 忽略校验的字段* @return: java.lang.StringBuffer* @author: chenzz* @date: 2020/4/22 23:36*/public static StringBuffer valiObjStringField(Object obj, String[] ignoreFields) {StringBuffer errMsg = new StringBuffer();try {Set<String> ignoreFieldSet = new HashSet<>();ignoreFieldSet.add("class");if (null != ignoreFields) {for (String field : ignoreFields) {if (StringUtils.isNotBlank(field)) {ignoreFieldSet.add(field);}}}PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(obj.getClass());for (PropertyDescriptor targetPd : propertyDescriptors) {if (ignoreFieldSet.contains(targetPd.getName())) {continue;}Method readMethod = targetPd.getReadMethod();if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {readMethod.setAccessible(true);}Object value = readMethod.invoke(obj);ValiInfo valiInfo = obj.getClass().getDeclaredField(targetPd.getName()).getAnnotation(ValiInfo.class);if (null == valiInfo) {continue;}String mean = valiInfo.mean();if (StringUtils.isBlank(mean)) {mean = targetPd.getName();}// 空置校验boolean isBlank = null == value || StringUtils.isBlank(value.toString());if (valiInfo.notBlank()) {if (isBlank) {errMsg.append(String.format("【%s】不能为空;", mean));continue;}} else {if (isBlank) {continue;}}// 如果是字符串才进行下面的校验if (!(value instanceof String)) {continue;}String strValue = value.toString();// 长度校验 最小长度为0int len = strValue.length();if (len < valiInfo.minLen()) {errMsg.append(String.format("【%s(%s)】长度不能小于%d个字符;", mean, strValue, valiInfo.minLen()));continue;}// 最大长度为2147483647if (len > valiInfo.maxLen()) {errMsg.append(String.format("【%s(%s)】长度不能超过%d个字符;", mean, strValue, valiInfo.maxLen()));Method writeMethod = targetPd.getWriteMethod();if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {writeMethod.setAccessible(true);}writeMethod.invoke(obj, strValue.substring(0, valiInfo.maxLen() - 1));continue;}if (strValue.matches(valiInfo.illegalCharRegExp())) {errMsg.append(String.format("【%s(%s)】中不能包含特殊字符;", mean, strValue));continue;}if (StringUtils.isNotBlank(valiInfo.regexp()) && !strValue.matches(valiInfo.regexp())) {errMsg.append(String.format("【%s(%s)】格式校验不通过;", mean, strValue));continue;}// 校验是否存在汉字if (StringUtils.isNotBlank(valiInfo.regexpChinese())) {char c[] = strValue.toCharArray();Pattern pattern = Pattern.compile(valiInfo.regexpChinese());boolean flag = false;for (int i = 0; i < c.length; i++) {Matcher matcher = pattern.matcher(String.valueOf(c[i]));if (matcher.matches()) {flag = true;}}if (flag) {errMsg.append(String.format("【%s(%s)】入参存在汉字;", mean, strValue));continue;}}// 码值校验if (StringUtils.isNotBlank(valiInfo.codeType())) {if (DictUtil.isNotDict(valiInfo.codeType().toUpperCase(), strValue)) {errMsg.append(String.format("【%s(%s)】不在代码表中;", mean, strValue));}}}return errMsg;} catch (IllegalAccessException e) {throw new BisException(BusinessConst.CALL_FAIL_TYPE_JC, "对象数据格式校验失败!");} catch (InvocationTargetException e) {throw new BisException(BusinessConst.CALL_FAIL_TYPE_JC, "对象数据格式校验失败!");} catch (NoSuchFieldException e) {throw new BisException(BusinessConst.CALL_FAIL_TYPE_JC, "对象数据格式校验失败!");}}
}
三、使用自定义注解:
自定义校验注解在代码中的应用
1、在dto中使用:
@Data
public class PsnInsureInfoQueryDTO implements Serializable {private static final long serialVersionUID = -3173331123860803536L;/*** 缴费开始年月*/@ValiInfo(mean = "缴费开始", minLen = 6, maxLen = 6, regexp = RegexpConst.REGEXP_YM)private String begnYm;/*** 单位编号*/@ValiInfo(mean = "单位编号", minLen = 1, maxLen = 40, illegalCharRegExp = RegexpConst.ILLEGAL_CHAR_REGEXP_EMPNO, regexpChinese = RegexpConst.REGEXP_CHINESE)private String empNo;/*** 险种类型*/@ValiInfo(mean = "险种类型", minLen = 1, maxLen = 3, codeType = "INSUTYPE", regexpChinese = RegexpConst.REGEXP_CHINESE)private String insutype;/*** 统筹区编码*/@ValiInfo(mean = "统筹区编码", minLen = 6, maxLen = 6, codeType = "ADMDVS_PLC", regexpChinese = RegexpConst.REGEXP_CHINESE)private String poolarea;/*** 参保状态*/@ValiInfo(mean = "参保状态", minLen = 1, maxLen = 3, codeType = "PSN_INSU_STAS", regexpChinese = RegexpConst.REGEXP_CHINESE)private String psnInsuStas;/*** 人员编号*/@ValiInfo(mean = "人员编号", minLen = 1, maxLen = 36, notBlank = true, regexpChinese = RegexpConst.REGEXP_CHINESE, illegalCharRegExp = RegexpConst.ILLEGAL_CHAR_REGEXP_PSNNO)private String psnNo;
}
2、在代码中,通过调用valiObjStringField方法来进行校验:
//control层的方法
public ResultVO insertHmcYcx(@RequestBody HmcinfoDTO hmcinfoDTO){//.......其它逻辑//调用方法,进行自定义注解的校验String errorMsg = ToolUtils.valiObjStringField(hmcinfoDTO).toString();if (StringUtils.isNotBlank(errorMsg)) {log.info(errorMsg);return new ResultVO(errorMsg);}//.......其它逻辑ResultVO vo = hmcBPO.insertHmc(hmcinfoDTO);return vo;
}
相关文章:
【java】实现自定义注解校验——方法一
自定义注解校验的实现步骤: 1.创建注解类,编写校验注解,即类似NotEmpty注解 2.编写自定义校验的逻辑实体类,编写具体的校验逻辑。(这个类可以实现ConstraintValidator这个接口,让注解用来校验) 3.开启使用自定义注解进…...
JavaScript基础入门03
目录 1.条件语句 1.1if 语句 1.1.1基本语法格式 1.1.2练习案例 1.2三元表达式 1.3switch 2.循环语句 2.1while 循环 2.2continue 2.3break 2.4for 循环 3.数组 3.1创建数组 3.2获取数组元素 3.3新增数组元素 3.3.1. 通过修改 length 新增 3.3.2. 通过下标新增 …...
P1903 [国家集训队] 数颜色 / 维护队列
带修改的莫队 带修改的莫队就是在基础莫队的基础上增加了一维属性,之前只需要维护l,r现在还需要维护一下时间t,排序还是先按照左端点块儿号排序,然后右端点块儿号排序,最后按时间排序。其它的都是差不多的。 #include…...
uniapp 请求接口的方式
在UniApp中,我们可以使用多种方式来发送请求接口。以下是几种常用的方式: 1、使用unmireuest方法:uni.reuest是uniApp提供的原生AP,可以发送HTTP请,我们可以通过传递一个图对象来设置请求的参数,RL、请求方法GET/POST…...
怎么查看当前vue项目,要求的node.js版本
要查看当前 Vue 项目所需的 Node.js 版本,你可以查看项目根目录下的 package.json 文件中的 engines 属性。该属性定义了项目所需的 Node.js 版本范围。 例如,以下是一个示例 package.json 文件: {"name": "my-vue-project&…...
QT5自适应
//集成屏幕自适应功能 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); DEVMODE NewDevMode; //获取屏幕设置中的分辨率 EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &NewDevMo…...
蓝桥杯官网练习题(日期问题)
题目描述 小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在 1960 年 1 月 1 日至 2059 年 12 月 31 日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采…...
PDF文件解析
一、PDF文件介绍 PDF是英文Portable Document Format缩写,就是可移植的意思,它是以PostScript语言图象模型为基础,无论在哪种打印机上都可保证精确的颜色和准确的打印效果,PostScript咱也不懂,估计和SVG的原理差不多吧…...
初识微服务技术栈
认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构,这些架构之间有怎样的差别呢? 导学: 了解微服务的优缺点;了解微服务架构的演变过程&am…...
windows 下运行正常,但是linux下报错 : Could not find or load main class
使用指令 "sed -i s/\r$// xxxxxxx.sh",将 .sh 文件中的 "\r" 全部替换成空白符,即可解决问题 转转:https://www.cnblogs.com/cmxbky1314/p/12096611.html...
MySQL 数据目录和 InnoDB 表空间补充知识:详细结构
1. 数据目录 在Ubuntu下,MySQL的数据目录为/var/lib/mysql 1.1 数据库在文件系统中的表示 (1)创建数据库时,会在数据目录下创建一个与数据库名同名的子目录。(除了information_schema这个系统数据外) &…...
移远EC600U-CN开发板 day02
1.QuecPythonLVGL显示图片 由于官方提供的显示图片函数使用失败,为了能在屏幕上显示图片,通过对出厂脚本的分析,成功使用LVGL显示图片 (1)代码 import lvgl as lv from tp import gt9xx from machine import LCD from machine import Pin …...
visual studio Python 配置QGIS(qgis)教程
visual studio Python 配置QGIS(qgis)教程 这个教程全网独一份啊,博主是自己摸索出来的。 visual studio Python 配置QGIS(qgis)环境一共分为两部: 第一步安装QGIS: 下载链接如下 https://www…...
第二证券:消费电子概念活跃,博硕科技“20cm”涨停,天龙股份斩获10连板
消费电子概念7日盘中再度拉升,到发稿,博硕科技“20cm”涨停,光大同创、波长光电涨超10%,易德龙、向阳科技、得润电子、天龙股份、同兴达等涨停。 博硕科技强势涨停,公司昨日在接受安排调研时表明,公司从上…...
petalinux 2022.2 在 ubantu18.04 下的安装
下载 Ubuntu下载: https://releases.ubuntu.com/18.04/ubuntu-18.04.6-desktop-amd64.iso petalinux 下载: https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/embedded-design-tools/2022-2.html 安装虚拟机 安装…...
【进程与线程】进程与线程 QA
进程与线程常见知识点: 1、什么是进程、线程,有什么区别? 进程是资源(CPU、内存等)分配的基本单位,线程是CPU调度和分配的基本单位程序执行的最小单位)。同一时间,如果CPU是单核,只有一个进程在执行,所谓…...
电脑风扇控制软件 Macs Fan Control Pro mac中文版功能介绍
Macs Fan Control mac是一款专门为 Mac 用户设计的软件,它可以帮助用户控制和监控 Mac 设备的风扇速度和温度。这款软件允许用户手动调整风扇速度,以提高设备的散热效果,减少过热造成的风险。 Macs Fan Control 可以在菜单栏上显示当前系统温…...
【13】c++11新特性 —>call_once
在某些特定情况下,某些函数只能在多线程环境下调用一次,比如:要初始化某个对象,而这个对象只能被初始化一次,就可以使用std::call_once()来保证函数在多线程环境下只能被调用一次。使用call_once()的时候,需…...
解决logstash插件logstash-outputs-mongodb一条数据失败后一直重复尝试
描述 从日志中读取数据时,有一条数据不符合规范,导致logstash读取数据插入时出错,而插件又无限尝试插入,导致堵塞。 解决方案 找到logstash文件夹目录,例如是:/data/logstash-7.3.2 cd /data/logstash-…...
【网络协议】聊聊HTTPDNS如何工作的
传统 DNS 存在哪些问题? 域名缓存问题 我们知道CND会进行域名解析,但是由于本地会进行缓存对应的域名-ip地址,所以可能出现过期数据的情况。 域名转发问题 出口 NAT 问题 域名更新问题 解析延迟问题 因为在解析DNS的时候,需要进行…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...
Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
第14节 Node.js 全局对象
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局…...
NoSQL——Redis配置与优化
目录 关系型&非关系型数据库 一、核心原理对比 二、核心特性对比 三、关键区别剖析 四、典型产品示例 总结 Redis Redis核心原理 核心特性 技术意义 配置文件解析 1. 基础配置 2. 持久化配置 3. 内存管理 4. 高可用配置 5. 性能调优 6.…...
CodeBuddy一腾讯内部已有超过 85% 的程序员正在使用de编程工具
大家好,我是程序员500佰,目前正在前往独立开发路线,我会在这里分享关于编程技术、独立开发、技术资讯以及编程感悟等内容。 如果本文能给你提供启发和帮助,还请留下你的一健三连,给我一些鼓励,谢谢。 本文直…...
我认为STM32输入只分为模拟输入 与 数字输入
核心概念解析 模拟输入 (Analog Input) 设计目的:直接连接模拟信号(如ADC采集电压、温度传感器输出) 硬件行为: ✅ 断开内部数字电路(施密特触发器禁用) ✅ 信号直通模拟外设(如ADC、运放&…...
