当前位置: 首页 > news >正文

Java Validator自定义日期范围验证注解:实现不超过一年的时间跨度校验

文章目录

    • 前言
    • 技术积累
      • 日期跨度计算的复杂性
      • Java 8+ 时间API的优势
      • Bean Validation规范
    • 实现方案
      • 核心验证逻辑
      • 自定义验证注解
      • 在VO类中的使用
      • controller使用VO
    • 总结

前言

在实际的业务开发中,我们经常需要对用户输入的时间范围进行校验,比如限制查询时间跨度不能超过一年。虽然Java提供了丰富的日期时间API,但针对特定业务规则的验证仍然需要我们自定义实现。这不,在最近的工作中需要实现dataTracking相关功能,免不了对数据处理跨度进行限制。为了方便在多个VO类中进行复用验证,我们实现了验证注解以满足实际业务需求。本文将详细介绍如何通过自定义注解和验证器来实现"时间跨度不超过一年"的校验逻辑,并探讨其中涉及的日期计算理论。

在这里插入图片描述

技术积累

日期跨度计算的复杂性

在处理日期跨度时,简单的天数计算(如365天=1年)并不准确,因为涉及到:
闰年的存在(每4年一个闰年,2月有29天)
不同月份天数的差异(28-31天不等)
跨年计算的复杂性

Java 8+ 时间API的优势

Java 8引入的 java.time 包提供了更加精确和易用的日期时间处理API:
LocalDate:表示不带时区的日期
ChronoUnit:用于计算日期时间单位之间的差异
plusYears():精确处理年份加法,自动处理闰年

Bean Validation规范

通过实现 ConstraintValidator 接口,我们可以创建自定义验证逻辑,这符合Java Bean Validation规范(JSR 380),保证了与Spring等框架的良好集成。

实现方案

实现 ConstraintValidator 接口,创建自己的验证类,并将验证逻辑与Spring注解结合起来。最后,只需要在需要的地方打上注解即可完成验证。

核心验证逻辑

定义一个DateRangeWithinOneYearValidator 实现ConstraintValidator 接口完成核心验证逻辑。

/*** DateRangeWithinOneYearValidator* @author senfel* @version 1.0* @date 2025/7/31 11:19*/
public class DateRangeWithinOneYearValidator implements ConstraintValidator<DateRangeWithinOneYear, LocalDate[]> {@Overridepublic boolean isValid(LocalDate[] dates, ConstraintValidatorContext context) {if (dates == null || dates.length != 2) {// 如果为空或长度不为2,让其他验证注解处理return true;}LocalDate start = dates[0];LocalDate end = dates[1];if (start == null || end == null) {// 如果日期为空,让其他验证注解处理return true;}// 确保开始日期不大于结束日期if (start.isAfter(end)) {return false;}// 检查是否跨越了超过一年的时间// 使用年份差来判断是否超过一年,而不是简单地使用365天if (start.getYear() + 1 < end.getYear()) {// 如果开始年份+1还小于结束年份,说明肯定超过一年了return false;} else if (start.getYear() + 1 == end.getYear()) {// 如果开始年份+1等于结束年份,需要检查具体日期// 比较月日,如果结束日期的月日小于开始日期的月日,则未超过一年if (end.getMonthValue() < start.getMonthValue() ||(end.getMonthValue() == start.getMonthValue() && end.getDayOfMonth() < start.getDayOfMonth())) {// 未超过一年return true;} else {// 可能超过一年,需要进一步检查LocalDate sameDateNextYear = start.plusYears(1);return !end.isAfter(sameDateNextYear);}} else {// 同一年内,肯定不超过一年return true;}}
}

自定义验证注解

自定义DateRangeWithinOneYear 注解,让我们在业务中更方便的插入验证逻辑。

/*** DateRangeWithinOneYear* @author senfel* @version 1.0* @date 2025/7/31 11:23*/
@Constraint(validatedBy = DateRangeWithinOneYearValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DateRangeWithinOneYear {String message() default "时间跨度不能超过一年";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

在VO类中的使用

在我们的接收验证类中使用验证注解

/*** DateRangeWithinOneYear* @author senfel* @version 1.0* @date 2025/7/31 11:30*/@Schema(description = "管理后台 - DpDataTrackingReqVO")
@Data
@ToString(callSuper = true)
public class DpDataTrackingReqVO {/*** 跟踪日期*/@Schema(description = "跟踪日期")@NotNull(message = "跟踪日期不能为空")@Size(min = 2, max = 2, message = "跟踪日期长度为 2")@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)@DateRangeWithinOneYear()private LocalDate[] trackingDate;/*** 区域*/private List<String> regionList;/*** 精品店*/private List<String> retailLocationCodeList;
}

controller使用VO

controller层引入我们的验证类,并使用@Valid开启验证。

/*** 获得CreationByCategory 统计数据* @param reqVo* @author senfel* @date 2025/7/31 15:00* @return cn.cce.yd.framework.common.pojo.CommonResult<java.util.List < cn.cce.yd.module.moi.controller.admin.dpdatatracking.vo.DpDataTrackingCreationByCategoryReqVO>>*/
@GetMapping("/selectCreationByCategory")
@Operation(summary = "获得CreationByCategory 统计数据")
@PreAuthorize("@ss.hasPermission('dp-data-tracking:creation-by-category:query')")
public CommonResult<List<DpDataTrackingCreationByCategoryReqVO>> selectCreationByCategory(@Valid DpDataTrackingReqVO reqVo) {return CommonResult.success( dpDataTrackingService.getDataTrackingCreationByCategory(reqVo));
}

效果展示
在这里插入图片描述
在这里插入图片描述

总结

通过自定义验证注解和验证器,我们成功实现了"时间跨度不超过一年"的业务规则校验。该方案采用Java 8的时间API,精确处理闰年和月份差异,并通过注解方式可在多个VO类中复用。而且改方案符合Bean Validation规范,与Spring等框架无缝集成,逻辑清晰,易于理解和后续维护。在实际应用中,这种自定义验证方式不仅能处理时间跨度校验,还可以扩展到其他复杂的业务规则验证,为构建高质量的Java应用提供了有效手段。

http://www.dtcms.com/a/310151.html

相关文章:

  • 面向对象三大特性---封装
  • FileInputStream 和 FileOutputStream 简介
  • ubuntu22.04系统入门 linux入门(二) 简单命令 多实践以及相关文件管理命令
  • 便携式综合气象观测仪:随时随地 “捕捉” 天气变化
  • PaddleOcr转onnx和推理
  • python:前馈人工神经网络算法之实战篇,以示例带学,弄明白神经网络算法应用的思路、方法与注意事项等
  • 高斯透镜公式(调整镜头与感光元件之间的距离时,使得不同距离的物体在感光元件上形成清晰的影像)
  • 企业级LLM智能引擎 的完整解决方案,整合了 SpringAI框架、RAG技术、模型控制平台(MCP)和实时搜索,提供从架构设计到代码实现的全面指南:
  • 【iOS】retain/release底层实现原理
  • Java 日期时间格式化模式说明
  • PTE之路--01
  • vivado扫盲 out-of-context(腾讯元宝)
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现围栏羊驼的检测识别(C#代码,UI界面版)
  • Android Material Components 全面解析:打造现代化 Material Design 应用
  • 数据处理四件套:NumPy/Pandas/Matplotlib/Seaborn速通指南
  • 如何在不依赖 Office 的情况下转换 PDF 为可编辑文档
  • lesson30:Python迭代三剑客:可迭代对象、迭代器与生成器深度解析
  • Redis 数据结构全景解析
  • Linux内核构建系统中的auto.conf与autoconf.h:原理与作用解析
  • 3D 管道如何实现流动的?
  • 基于SpringBoot+MyBatis+MySQL+VUE实现的疗养院管理系统(附源码+数据库+毕业论文+远程部署)
  • cesium FBO(三)渲染到Canvas(灰度图效果)
  • 【OneAPI】网页搜索API和网页正文提取API
  • Lombok常用注解及功能详解
  • oracle的安全加密有哪些?
  • Python文件对比利器:filecmp模块详解
  • 学习嵌入式第十七天
  • Vue项目使用ssh2-sftp-client实现打包自动上传到服务器(完整教程)
  • 10.Linux 用户和组的管理
  • 【HL7】.aECG与.hl7文件的关系和区别