在List/数组上使用<ui:repeat>、<h:inputText>与<f:validateWholeBean>的疑问
JSF <f:validateWholeBean> 数组类级验证的问题解析
问题场景
我基于<f:validateWholeBean>的类级Bean验证(比如密码确认)场景,尝试用<ui:repeat>+<h:inputText>收集动态数量的数组值,测试环境如下:
- 实体类
TestData:包含int类型属性a、b,已实现转换器与验证器 - 后台Bean
TestController:实现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>的绑定对象**不匹配:
<f:validateWholeBean>会克隆原Bean生成候选实例,用于类级验证。若clone()方法对testData数组做了深拷贝(即克隆每个TestData元素),候选Bean的数组元素与原Bean的元素是独立实例<ui:repeat>绑定的是原Bean的testData数组,提交时直接修改的是原Bean的元素属性,候选Bean的数组元素完全不受影响,因此isValid()中看到的是旧值- 若修改
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
相关产品推荐
相关产品推荐

