从使用
f:valdiateWholeBean
进行类级别 bean 验证的无处不在的密码示例开始,我尝试通过 <ui:repeat>
和 <h:inputText>
收集列表或数组中的动态数量的值。为了简单起见,我使用:
class TestData{
int a;
int b;
}
作为数据。类型
TestData
的转换器和验证器确实存在。我的 Backingbean 看起来像这样:
class TestController implements Clonable{
...
# just to verify, that class level validation works in general
String pw1;
String pw2;
# test for scalar values
TestData x1;
# test for non-scalar Data
TestData[] testData;
...
public TestController clone() {
...
clone.testData=new TestData[testData.length];
for(int i=0;i<clone.testData.length;i++){
clone.testData[i]=new TestData();
}
return clone;
}
}
克隆方法按预期被调用,我为数据腾出了空间。对应的Faclet:
<h:form id="testForm">
<ul>
<li>
<h:inputText id="pw1Input" value="#{testController.pw1}">
<f:validateBean validationGroups="ValidationGroup"/>
</h:inputText>
</li>
<li>
<h:inputText id="pw2Input" value="#{testController.pw2}">
<f:validateBean validationgroups="ValidationGroup"/>
</h:inputText>
</li>
<li>
<h:inputText value="#{testController.x1}">
<f:validator validatorId="testDataFacesValidator"/>
<f:validateBean validationGroups="ValidationGroup"/>
</h:inputText>
</li>
<ui:repeat value="#{testController.testData}" varStatus="status" var="data">
<li>
<h:inputText id="valInput_#{status.index}" value="#{testController.testData[status.index]}">
<f:validator validatorId="testDataFacesValidator"/>
<f:validateBean validationGroups="ValidationGroup"/>
</h:inputText>
</li>
</ui:repeat>
</ul>
<h:commandButton value="save" action="#{testController.save}">
<f:ajax execute="@form" render="globalMsg testForm"/>
</h:commandButton>
<f:validateWholeBean value='#{testController}' validationGroups="ValidationGroup"/>
</h:form>
当我输入数据时,我可以从日志消息中看到,
TestData
的转换和验证按预期进行。约束验证器是:
public class ConstValidator implements ConstraintValidator<UniqueData, DataHolder> {
@Override
public void initialize(UniqueData constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
logger.info("[initialize]");
}
@Override
public boolean isValid(DataHolder dataHolder, ConstraintValidatorContext constraintValidatorContext) {
String pw1=dataHolder.getPw2();
logger.info("[isValid: pw2: "+pw2+" ]"); // getting the value I entered
TestData x1=dataHolder.getX1();
logger.info("[isValid: x1: "+x1+" ]");// getting my value
TestData[] data=dataHolder.getTestData();
for(TestData td:data){
logger.info("[isValid: testData: "+td+" ]");// getting initial, unmodifyed data
...
return true;
}
}
最后,我的 Backingbean 获取我输入字符串的所有值、标量测试数据以及数组中的所有修改值。
[文档][2] 说:...如果 bean 实现 Cloneable,则克隆 bean ...用候选值填充复制的 bean....
我问自己:数组的这种“填充”是如何发生的。我修改了 Backingbean 中
TestData[]
的 getter/setter 以获得深层副本,但这让情况变得更糟。
我从这个答案了解到,嵌套属性不能以这种方式验证。答案也给出了一个很好的替代解决方案。