为什么JSF要对隐藏的私有字段应用Bean验证?
我在Hibernate验证器和JSF中遇到了一些令人惊讶的行为。我想知道这种行为是一种错误,还是我自己期望的一种误解 我有这个Facelets页面:为什么JSF要对隐藏的私有字段应用Bean验证?,jsf,jakarta-ee,bean-validation,hibernate-validator,Jsf,Jakarta Ee,Bean Validation,Hibernate Validator,我在Hibernate验证器和JSF中遇到了一些令人惊讶的行为。我想知道这种行为是一种错误,还是我自己期望的一种误解 我有这个Facelets页面: <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> &
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h:form>
<h:inputText value="#{backingBean.someClass.someField}"/>
<h:commandButton value="submit" action="#{backingBean.submit1()}"/>
</h:form>
</h:body>
</html>
我有一个后盾:
import java.util.Set;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.hibernate.validator.constraints.NotEmpty;
@ManagedBean
@RequestScoped
public class BackingBean {
private SomeClass someClass = new SomeSubClass();
public SomeClass getSomeClass() {
return someClass;
}
public void setSomeClass(SomeClass someClass) {
this.someClass = someClass;
}
public void submit1() {
System.out.println("BackingBean: " + someClass.getSomeField());
((SomeSubClass) someClass).submit2();
}
public static class SomeClass {
private String someField;
public String getSomeField() {
return someField;
}
public void setSomeField(String someField) {
this.someField = someField;
}
}
public static class SomeSubClass extends SomeClass {
@NotEmpty
private String someField;
private void submit2() {
System.out.println("SomeSubClass: " + someField);
}
}
public static void main(String[] args) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
SomeClass someClass = new SomeSubClass();
someClass.setSomeField("ham");
Set<ConstraintViolation<SomeClass>> errors = validator.validate(someClass);
for (ConstraintViolation<SomeClass> error : errors) {
System.out.println(error);
}
}
}
import java.util.Set;
导入javax.faces.bean.ManagedBean;
导入javax.faces.bean.RequestScope;
导入javax.validation.ConstraintViolation;
导入javax.validation.validation;
导入javax.validation.Validator;
导入javax.validation.ValidatorFactory;
导入org.hibernate.validator.constraints.NotEmpty;
@ManagedBean
@请求范围
公共类BackingBean{
private SomeClass SomeClass=new SomeSubClass();
公共SomeClass getSomeClass(){
返回某个类;
}
公共无效设置SomeClass(SomeClass SomeClass){
this.someClass=someClass;
}
公开无效提交1(){
System.out.println(“BackingBean:+someClass.getSomeField());
((SomeSubClass)someClass.submit2();
}
公共静态类{
私有字符串字段;
公共字符串getSomeField(){
返回someField;
}
public void setSomeField(字符串someField){
this.someField=someField;
}
}
公共静态类SomeSubClass扩展了SomeClass{
@空空如也
私有字符串字段;
私人无效提交2(){
System.out.println(“SomeSubClass:+someField”);
}
}
公共静态void main(字符串[]args){
ValidatorFactory=Validation.buildDefaultValidatorFactory();
Validator Validator=factory.getValidator();
SomeClass SomeClass=新的SomeSubClass();
someClass.setSomeField(“ham”);
Set errors=validator.validate(someClass);
for(ConstraintViolation错误:错误){
系统输出打印项次(错误);
}
}
}
请注意,SomeSubClass.someField
与父类中的私有变量具有相同的名称。这不重要,因为您不能对私有字段进行阴影处理
有两点需要注意:
BackingBean
中运行main
方法,验证程序将始终返回验证错误(消息“可能不为空”),因为SomeSubClass.someField
始终为空。我认为这种行为是正确的SomeSubClass.someField
的值仍然为空。我觉得这种行为不正确。
- 如果将
的名称更改为SomeSubClass.someField
,则可以提交带有空值的表单。我觉得这种行为不正确someField2
@NotEmpty
验证器应用于父类中同名字段,但Hibernate验证器在单独测试时不会显示此行为
有人能解释这种行为吗?这是一个错误,还是我自己的期望中的误解
使用:
- GlassFish服务器开源版本3.1.2.2
- 莫哈拉2.1.6
- Hibernate验证程序4.3.0.Final
- JSF使用EL获取/设置符合Javabean规则的模型值。EL使用反射来检查和调用公共getter和setter。换句话说,
{backingBean.someClass.someField}
实际上使用getSomeField()
和setSomeField()
来获取/设置模型/提交的值。被操纵的字段实际上就是SomeClass
中的字段
Bean验证使用反射来检查字段,并忽略公共getter/setter的存在。换句话说,{backingBean.someClass.someField}
的BV实际上发生在SomeSubClass
上
public static class SomeSubClass extends SomeClass {
@NotEmpty
private String someField;
private void submit2() {
System.out.println("SomeSubClass: " + someField);
}
@Override
public String getSomeField() {
return someField;
}
@Override
public void setSomeField(String someField) {
this.someField = someField;
}
}
这解释了一切。但我同意这是令人困惑的,似乎“不正确”。当您在SomeSubClass
中重写getter和setter时,它将按预期工作
public static class SomeSubClass extends SomeClass {
@NotEmpty
private String someField;
private void submit2() {
System.out.println("SomeSubClass: " + someField);
}
@Override
public String getSomeField() {
return someField;
}
@Override
public void setSomeField(String someField) {
this.someField = someField;
}
}
这样,EL将看到它并将其用作模型值
然而,这很尴尬(一个只调用super的@Override
将翻转静态代码风格的分析工具,如Sonar、PMD、Findbugs等)。更好的替代方法是将@NotEmpty
移动到被重写的getter:
public static class SomeSubClass extends SomeClass {
private void submit2() {
System.out.println("SomeSubClass: " + getSomeField());
}
@Override
@NotEmpty
public String getSomeField() {
return super.getSomeField();
}
}
BV也支持此构造,因此它将继续工作。@BalusC,感谢您的编辑。在您的评论之后,我单独测试了Hibernate验证器,发现就我而言,它的行为是正确的。我已经在我的问题中添加了一个JSF验证阶段与Hibernate Validator的比较,它演示了这个问题。让我看看我是否理解。在设置模型值之前,JSF使用Bean Validation的查找机制(使用反射)查找验证器,然后JSF将提交的值传递给验证器。如果通过验证,则设置模型值。这与在模型上设置值,然后对整个模型应用Bean验证形成对比,在这种情况下,
SomeSubClass.someField
上的@NotEmpty
总是失败。确实,EL总是通过getter/setter访问模型值,而不是通过字段。此外,对于EL来说,字段不一定存在。