You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在List/数组上使用<ui:repeat>、<h:inputText>与<f:validateWholeBean>的疑问

JSF <f:validateWholeBean> 数组类级验证的问题解析

问题场景

我基于<f:validateWholeBean>的类级Bean验证(比如密码确认)场景,尝试用<ui:repeat>+<h:inputText>收集动态数量的数组值,测试环境如下:

  • 实体类TestData:包含int类型属性a、b,已实现转换器与验证器
  • 后台BeanTestController:实现Cloneable接口,含类级验证用的pw1/pw2字符串、单个TestData类型标量属性x1,以及TestData[]数组属性testData;clone()方法已实现并为数组分配空间
  • Facelet表单:包含密码输入框、单个TestData输入框,通过<ui:repeat>循环渲染testData数组的元素输入框,每个输入框配置验证器与<f:validateBean>,表单同时通过<f:validateWholeBean>对TestController做类级验证

遇到的问题:
输入数据后,TestData的转换、验证日志显示正常,但在ConstraintValidator的isValid()方法中,testData数组元素仍是初始旧值;最终后台Bean却能正确接收所有修改后的值。尝试修改TestData[]的getter/setter做深拷贝,问题反而恶化。

原因分析

核心问题出在候选Bean的克隆逻辑与**<ui:repeat>的绑定对象**不匹配:

  1. <f:validateWholeBean>会克隆原Bean生成候选实例,用于类级验证。若clone()方法对testData数组做了深拷贝(即克隆每个TestData元素),候选Bean的数组元素与原Bean的元素是独立实例
  2. <ui:repeat>绑定的是原Bean的testData数组,提交时直接修改的是原Bean的元素属性,候选Bean的数组元素完全不受影响,因此isValid()中看到的是旧值
  3. 若修改TestData[]的getter/setter做深拷贝,<ui:repeat>会遍历克隆后的临时数组,JSF修改的是临时数组的元素,原Bean的数组根本不会被更新,导致最终原Bean拿不到新值,这就是“情况更糟”的原因

解决方案

1. 修正clone()方法为浅拷贝

确保候选Bean的testData数组元素与原Bean的元素是同一个引用,这样<ui:repeat>修改原Bean元素时,候选Bean的元素会同步更新:

@Override
public Object clone() throws CloneNotSupportedException {
    TestController cloned = (TestController) super.clone();
    // 对数组做浅拷贝,元素引用原Bean的TestData实例
    cloned.testData = testData.clone();
    return cloned;
}

2. 移除数组getter/setter的深拷贝逻辑

不要让testData的getter返回克隆数组,也不要在setter中做深拷贝,确保<ui:repeat>直接绑定原Bean的数组实例,JSF提交时能直接修改原Bean的元素。

关键说明

JSF官方文档提到的“用候选值填充复制后的Bean”,对于数组类型属性,默认只会处理数组引用,不会自动克隆数组元素。因此类级验证场景下,数组属性的克隆需用浅拷贝,才能让候选Bean同步获取到<ui:repeat>提交的修改值。

内容的提问来源于stack exchange,提问作者Thomas P

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 02:10:56