为什么 JSF 应用阴影私有字段的 Bean 验证?
Why is JSF applying the Bean Validation of a shadowed private field?
我在 Hibernate Validator 和 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">
<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>
我有这个支持 bean:
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);
}
}
}
请注意 SomeSubClass.someField
与父 class 中的私有变量同名。这应该无关紧要,因为您不能隐藏私有字段。
注意两点:
- 如果您 运行
BackingBean
中的 main
方法,验证器将始终 return 验证错误(消息 "may not be empty"),因为 SomeSubClass.someField
始终为空。这种行为对我来说似乎是正确的。
- 如果您提交带有空白值的表单,您将收到 "may not be empty" 错误消息;如果你输入一个值,它将通过验证,但你会在控制台中看到
SomeSubClass.someField
的值仍然为空。这种行为对我来说似乎是不正确的。
- 如果您将
SomeSubClass.someField
的名称更改为 someField2
,您可以提交带有空值的表单。这种行为对我来说似乎是不正确的。
看来 JSF 的验证阶段和 Hibernate 验证器不同意这种行为。 JSF 将子 class 中私有字段的 @NotEmpty
验证器应用于父 class 中的同名字段,但 Hibernate Validator 在单独测试时不会显示此行为.
任何人都可以解释这种行为吗?是bug,还是自己的预期有误?
使用:
- GlassFish Server 开源版 3.1.2.2
- Mojarra 2.1.6
- Hibernate 验证器 4.3.0.Final
JSF 使用 EL get/set 模型值符合 Javabean 规则。 EL 使用反射来检查和调用 public getter 和 setter。换句话说,#{backingBean.someClass.someField}
实际上使用 getSomeField()
和 setSomeField()
来 get/set model/submitted 值。被操作的字段实际上是 SomeClass
.
中的字段
Bean Validation 使用反射来检查字段并忽略 public getters/setters 的存在。也就是说,#{backingBean.someClass.someField}
的BV实际上发生在SomeSubClass
。
这说明了一切。但我同意这令人困惑并出现 "incorrect"。当您还覆盖 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 也支持这种结构,所以它会继续工作。
我在 Hibernate Validator 和 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">
<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>
我有这个支持 bean:
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);
}
}
}
请注意 SomeSubClass.someField
与父 class 中的私有变量同名。这应该无关紧要,因为您不能隐藏私有字段。
注意两点:
- 如果您 运行
BackingBean
中的main
方法,验证器将始终 return 验证错误(消息 "may not be empty"),因为SomeSubClass.someField
始终为空。这种行为对我来说似乎是正确的。 - 如果您提交带有空白值的表单,您将收到 "may not be empty" 错误消息;如果你输入一个值,它将通过验证,但你会在控制台中看到
SomeSubClass.someField
的值仍然为空。这种行为对我来说似乎是不正确的。- 如果您将
SomeSubClass.someField
的名称更改为someField2
,您可以提交带有空值的表单。这种行为对我来说似乎是不正确的。
- 如果您将
看来 JSF 的验证阶段和 Hibernate 验证器不同意这种行为。 JSF 将子 class 中私有字段的 @NotEmpty
验证器应用于父 class 中的同名字段,但 Hibernate Validator 在单独测试时不会显示此行为.
任何人都可以解释这种行为吗?是bug,还是自己的预期有误?
使用:
- GlassFish Server 开源版 3.1.2.2
- Mojarra 2.1.6
- Hibernate 验证器 4.3.0.Final
JSF 使用 EL get/set 模型值符合 Javabean 规则。 EL 使用反射来检查和调用 public getter 和 setter。换句话说,#{backingBean.someClass.someField}
实际上使用 getSomeField()
和 setSomeField()
来 get/set model/submitted 值。被操作的字段实际上是 SomeClass
.
Bean Validation 使用反射来检查字段并忽略 public getters/setters 的存在。也就是说,#{backingBean.someClass.someField}
的BV实际上发生在SomeSubClass
。
这说明了一切。但我同意这令人困惑并出现 "incorrect"。当您还覆盖 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 也支持这种结构,所以它会继续工作。