讲师中心 微信公众号
AI工具推荐 视频效率加速

如何利用反射实现通用的对象校验框架_多规则链式匹配

阿枫姑娘_1080

阿枫姑娘_1080

发布时间:2026-02-12 16:45:27

|

848人浏览过

|

来源于php中文网

原创

必须用getDeclaredFields()并设setAccessible(true)才能获取private字段;校验注解须加在字段而非getter上;继承需递归遍历父类;Kotlin字段名可能被重命名;group和payload需显式传递;ConstraintValidator泛型不可省略;@Valid必须显式标注才启用级联校验。

如何利用反射实现通用的对象校验框架_多规则链式匹配

反射获取字段和注解时,getDeclaredFields() 和 getFields() 别混用

校验框架第一步是扫字段,但很多人一上来就用 getFields(),结果 private 字段全丢了——它只返回 public 成员。必须用 getDeclaredFields(),再手动设 setAccessible(true) 才能读 private 字段值。

常见错误现象:NullPointerException 或校验跳过某些字段,实际是字段根本没被扫描到。

  • 所有校验注解(如 @NotNull、@Min)都该加在字段上,不是 getter 方法上,否则反射取不到值
  • 如果类有继承,getDeclaredFields() 不会返回父类字段,得递归调用 getSuperclass() 向上遍历
  • 注意 Kotlin 编译后字段名可能带 $ 或重命名(如 name → name$delegate),Java 反射直接按源码名取会失败

规则链执行顺序依赖注解的 groups 和 payload,但多数人忽略验证上下文传递

所谓“链式匹配”,不是硬编码 if-else,而是靠注解元信息动态组装规则链。比如 @NotBlank(groups = Create.class) 和 @NotBlank(groups = Update.class) 必须在触发校验时显式传入 group 类型,否则默认不生效。

使用场景:同一对象在创建和更新时校验逻辑不同,靠 group 分离规则,而不是写两套校验器。

  • 调用 validator.validate(target, Create.class) 才会命中 Create.class 组的规则;漏传参数等于白标注解
  • payload 通常用于携带自定义校验上下文(如租户 ID、业务通道),但标准 Validator 不解析它,得自己在 ConstraintValidator 实现里从 ConstraintValidatorContext 提取
  • 多个注解在同一字段上时,执行顺序由 JVM 返回的注解数组顺序决定,不保证声明顺序,别依赖“先写哪个就先校验”

自定义 ConstraintValidator 里拿不到运行时值?检查 initialize() 是否覆盖了泛型参数

写一个 CheckInListValidator 校验字段是否在枚举白名单里,结果 isValid() 里 value 总是 null——大概率是 initialize() 方法签名写成了 initialize(CheckInList constraintAnnotation),而没继承泛型父类。

正确写法必须显式声明泛型类型,否则运行时擦除导致约束注解实例为空:

public class CheckInListValidator implements ConstraintValidator<CheckInList, String> {
    private String[] allowedValues;

    @Override
    public void initialize(CheckInList constraintAnnotation) {
        this.allowedValues = constraintAnnotation.value(); // 这里才能取到注解属性
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && Arrays.asList(allowedValues).contains(value);
    }
}
  • 忘写 implements ConstraintValidator<xxx yyy></xxx> 中的泛型,initialize() 参数就是 raw type,反射取不到注解值
  • 如果校验逻辑涉及 Spring Bean(如查数据库),不能在 ConstraintValidator 里直接 @Autowired,得通过 ValidationConfiguration 注入或用 ApplicationContext.getBean()
  • 性能影响:每次校验都会 new 一个 validator 实例,所以 initialize() 里适合做一次性的配置解析,别放耗时操作

嵌套对象校验失效?@Valid 必须显式标注,且不能只靠级联

用户对象里有个 Address address 字段,Address 类也有 @NotBlank 注解,但改了 address.street 为 null 却没报错——因为没在 address 字段上加 @Valid。

JSR-303 的级联校验不是默认开启的,必须主动标注,而且只对字段/方法有效,对集合元素要配合 @Valid + @Size 等组合使用。

  • @Valid 只作用于当前字段,不会自动递归进 List 的每个元素;List 校验需写成 List(Java 8+ type annotation)或用 @Valid 配合 @Size 在字段上
  • Map 的 value 校验同理,Map<string user></string> 才行;key 不支持 @Valid
  • Spring MVC 里 Controller 参数用 @Valid,但嵌套对象仍需字段级 @Valid,两者缺一不可
反射做通用校验,最麻烦的从来不是怎么扫字段,而是怎么让注解元数据、运行时值、上下文环境三者对齐。少设一个 groups、漏标一个 @Valid、或者泛型擦除没处理好,整条链就静默失效——它不会报错,只会假装校验过了。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

相关专题

更多
Kotlin协程编程与Spring Boot集成实践
Kotlin协程编程与Spring Boot集成实践

本专题围绕 Kotlin 协程机制展开,深入讲解挂起函数、协程作用域、结构化并发与异常处理机制,并结合 Spring Boot 展示协程在后端开发中的实际应用。内容涵盖异步接口设计、数据库调用优化、线程资源管理以及性能调优策略,帮助开发者构建更加简洁高效的 Kotlin 后端服务架构。

391

2026.02.12

Java Kotlin协程与异步编程实战
Java Kotlin协程与异步编程实战

本专题围绕 Kotlin 协程与异步编程展开,讲解挂起函数、协程作用域、异步任务调度及并发控制方法。通过实际项目案例,帮助开发者提升后端服务性能,实现高效异步任务处理与稳定性保障。

179

2026.04.13

Kotlin Android开发教程大全
Kotlin Android开发教程大全

系统讲解 Google 官方推荐的 Android 开发首选语言 Kotlin 的核心知识与工程实践,涵盖 Kotlin 简洁语法(空安全/数据类/扩展函数/解构声明/作用域函数)与 Java 互操作、集合操作与函数式编程、协程(Coroutines)异步编程(suspend/launch/async/Flow)、Jetpack Compose 声明式 UI 开发(状态管理/组合函数/导航/动画)、MVVM / MVI 架构设计与 Vi

268

2026.06.03

golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

519

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

616

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1216

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

791

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1786

2024.05.21

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

60

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn