news 2026/7/13 2:45:30

自定义跨字段校验必填注解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
自定义跨字段校验必填注解

注解类

package com.xxx.common.core.annotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @Author zibocoder
* @Date 2026/04/13
* @Description 跨字段校验必填注解
*/
@Documented
@Constraint(validatedBy = CrossFieldRequiredValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CrossFieldRequired {
String message() default "该字段为必填项";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String dependField();
String expectedValue();
String targetField();
// 触发方式, 默认非空触发
TriggerType triggerType() default TriggerType.NOT_EMPTY;
enum TriggerType {
NOT_EMPTY,
NOT_EQUALS,
EQUALS
}
}

注解验证器类

package com.xxx.common.core.annotation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;
/**
* @Author zibocoder
* @Date 2026/04/13
* @Description 跨字段校验必填注解验证器
*/
public class CrossFieldRequiredValidator implements ConstraintValidator<CrossFieldRequired, Object> {
// 被依赖的字段
private String dependField;
// 被依赖字段的期望值
private String expectedValue;
// 目标字段
private String targetField;
// 触发方式
private CrossFieldRequired.TriggerType triggerType;
@Override
public void initialize(CrossFieldRequired constraintAnnotation) {
this.dependField = constraintAnnotation.dependField();
this.expectedValue = constraintAnnotation.expectedValue();
this.targetField = constraintAnnotation.targetField();
this.triggerType = constraintAnnotation.triggerType();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
try {
Field dependFld = getField(value.getClass(), dependField);
if (dependFld == null) {
return true;
}
dependFld.setAccessible(true);
Object dependValue = dependFld.get(value);
// 是否应该校验
boolean shouldValidate = false;
if (triggerType == CrossFieldRequired.TriggerType.NOT_EMPTY) {
shouldValidate = dependValue != null && !dependValue.toString().trim().isEmpty();
} else if (triggerType == CrossFieldRequired.TriggerType.NOT_EQUALS) {
shouldValidate = dependValue != null && !expectedValue.equals(dependValue.toString());
} else if (triggerType == CrossFieldRequired.TriggerType.EQUALS) {
shouldValidate = dependValue != null && expectedValue.equals(dependValue.toString());
}
if (shouldValidate) {
Field targetFld = getField(value.getClass(), targetField);
if (targetFld == null) {
return true;
}
targetFld.setAccessible(true);
Object targetValue = targetFld.get(value);
if (isEmpty(targetValue)) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
.addPropertyNode(targetField)
.addConstraintViolation();
return false;
}
}
return true;
} catch (Exception e) {
return true;
}
}
/**
* 判断对象是否为空
*
* @param value 对象
* @return true:为空 false:不为空
*/
private boolean isEmpty(Object value) {
if (value == null) {
return true;
}
if (value instanceof String) {
return ((String) value).trim().isEmpty();
}
return false;
}
/**
* 递归查找字段,支持继承场景
*
* @param clazz 类
* @param fieldName 字段名
* @return 字段
*/
private Field getField(Class<?> clazz, String fieldName) {
while (clazz != null) {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
return null;
}
}

使用示例

package com.xxx.domain.form;
import com.xxx.common.core.annotation.CrossFieldRequired;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @Author zibocoder
* @Date 2026/04/13
* @Description
*/
// 类级别的自定义验证注解,实现跨字段条件校验:当 firstName 字段的值为“张”时,lastName 字段不能为空(null 或空串)。
@CrossFieldRequired(
dependField = "firstName",
targetField = "lastName",
expectedValue="张",
triggerType = CrossFieldRequired.TriggerType.NOT_EMPTY, //默认可不写
message = "姓氏不为空时,名字也不能为空")
@Data
public class UserAddForm {
@NotBlank(message = "姓氏不能为空")
private String firstName;
private String lastName;
}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/13 4:58:27

从“任意文件复制“深挖Java I/O:字符流与字节流的本质抉择

一、问题的引入&#xff1a;为什么文本复制和任意文件复制要分开讨论&#xff1f;初学Java I/O时&#xff0c;很多人&#xff08;包括我&#xff09;都曾困惑&#xff1a;既然FileInputStream/FileOutputStream能复制任何文件&#xff0c;为什么还要有FileReader/FileWriter&am…

作者头像 李华
网站建设 2026/7/13 4:58:23

中台建了、仓库搭了、报表做了,为什么业务还是要Excel?——从DAMA知识体系看数据中台治理落地的工程方法论

摘要&#xff1a; 许多企业投入数百万建设数据中台&#xff0c;最终却回到Excel手工处理的起点。本文从DAMA-DMBOK知识体系出发&#xff0c;剖析数据中台项目中最易出现短板的五个知识领域&#xff0c;并结合龙石数据的"理采存管用"工程方法论&#xff0c;探讨如何将…

作者头像 李华
网站建设 2026/7/13 5:06:13

奔驰STAR3 E/架构 高速视频链接(HSVL)

下面对比总结一下STAR2 E/E架构和STAR3 E/E架构的不同。 STAR2 E/E架构是伴随上一代S级轿车&#xff08;车型代号W222&#xff09;上市&#xff0c;于2013年推出的&#xff0c;STAR2结构受限于以下总线系统&#xff1a; 局域互联网&#xff08;LIN&#xff09;总线 CAN 2.0 F1e…

作者头像 李华
网站建设 2026/7/12 17:10:23

专科大数据专业怎么专升本?升学路径+志愿规划+能力提升全攻略

每年高考志愿填报阶段&#xff0c;很多分数有限的同学会纠结&#xff1a;选专科大数据专业&#xff0c;未来还有出路吗&#xff1f;能不能顺利升本、逆袭就业&#xff1f; 答案是&#xff1a;完全可以&#xff0c;且性价比极高。 大数据是人工智能时代的核心刚需专业&#xff0…

作者头像 李华
网站建设 2026/7/13 5:05:21

XR 沉浸式娱乐在文旅行业的发展前景

XR沉浸式娱乐为什么会成为文旅行业的新增长方向&#xff1f;XR沉浸式娱乐正在成为文旅行业升级的重要方向。对于景区、主题乐园、城市夜游、灯会活动和文旅综合体来说&#xff0c;游客需求已经从“看景观、拍照片”转向“进场景、强互动、深体验”。传统观赏式项目很难持续拉长…

作者头像 李华