2015-01-19 3 views
1

나는 Hibernate Validator와 JSF에서 놀라운 행동을 경험했다. 그 행동이 버그인지 또는 내 자신의 기대에 대한 오해인지 알고 싶습니다.JSF가 숨겨진 비공개 필드의 Bean 유효성 검사를 적용하는 이유는 무엇입니까?

나는이 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> 

을 그리고 나는이 백업 콩이 : SomeSubClass.someField는 부모 클래스의 private 변수와 같은 이름을 가진

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); 
     } 

    } 
} 

하는 것으로합니다. 이것은 사적인 영역을 그늘지게 할 수 없으므로 중요하지 않습니다.

두 가지 참고 사항 : 당신이 BackingBean에서 main 방법을 실행하는 경우

  1. 은 발리는 항상 유효성 검사 오류 (메시지 "비어있을 수 없습니다") SomeSubClass.someField는 항상 null이기 때문에 반환됩니다. 이 문제는 나에게 맞는 것 같습니다.
  2. 빈 값으로 양식을 제출하면 "비어 있지 않을 수도 있습니다"라는 오류 메시지가 나타납니다. 값을 입력하면 유효성 검사를 통과하지만 SomeSubClass.someField 값이 여전히 null임을 콘솔에 알 수 있습니다. 이 문제는 나에게 잘못된 것 같습니다.
    • SomeSubClass.someField의 이름을 someField2으로 변경하면 양식을 빈 값으로 제출할 수 있습니다. 이 문제는 나에게 잘못된 것 같습니다.

JSF의 검증 단계 및 최대 절전 모드 검사기는이 동작에 동의하지 않을 것으로 보인다. JSF는 서브 클래스의 private 필드의 유효성 검사기를 부모 클래스의 동일한 이름의 필드에 적용하지만 Hibernate Validator는 격리 된 상태로 테스트 할 때이 동작을 표시하지 않습니다.

누구든지이 동작을 설명 할 수 있습니까? 그것은 버그입니까, 아니면 내 자신의 기대에 대한 오해입니까?

사용 :

  • 글래스 피쉬 서버 오픈 소스 버전 3.1.2.2
  • 인 Mojarra 2.1.6
  • 최대 절전 모드 검사기 4.3.0.Final을
+0

@BalusC, 편집 해 주셔서 감사합니다. 귀하의 의견을 따를 때, 나는 Hibernate Validator를 고립 상태로 시험해 보았고, 그것은 내가하는 한 그것이 올바른 행동임을 알았다. 나는 JSF의 Validation Phase와 Hibernate Validator의 비교를이 문제를 보여주는 나의 질문에 추가했다. – DavidS

+0

나는 본다. 좋은 업데이트! – BalusC

답변

1

JSF는/세트 얻을 EL을 사용하여 모델 값은 Javabean 규칙을 준수합니다. EL은 리플렉션을 사용하여 공용 getter 및 setter를 검사하고 호출합니다. 즉 #{backingBean.someClass.someField}은 실제로 getSomeField()setSomeField()을 사용하여 모델/제출 된 값을 가져 오거나 설정합니다. 조작되는 필드는 실제로는 SomeClass의 필드입니다.

빈 검증은 필드를 검사하기 위해 리플렉션을 사용하고 공용 getters/setter의 존재를 무시합니다. 즉, #{backingBean.someClass.someField}의 BV는 실제로 SomeSubClass에서 발생합니다.

이렇게 모든 것을 설명합니다. 그러나 나는 이것이 혼란스럽고 "틀린"것처럼 보임에 동의합니다. SomeSubClass에 getter 및 setter도 재정의하면 예상대로 작동합니다.

이렇게하면 EL이이를보고 모델 값으로 사용합니다.

그러나 이것은 단지 이라고하는 @Override을 호출하면 소나, PMD, Findbugs 등과 같은 정적 코드 스타일 분석 도구를 뒤집을 수 있습니다.

public static class SomeSubClass extends SomeClass { 

    private void submit2() { 
     System.out.println("SomeSubClass: " + getSomeField()); 
    } 

    @Override 
    @NotEmpty 
    public String getSomeField() { 
     return super.getSomeField(); 
    } 
} 

BV는이 구조를 지원하므로이 작업있을 것이다 : 더 나은 대안은 재정의 게터에 @NotEmpty를 이동하는 것입니다.

+0

내가 이해하면 보자. _Prior는 모델 값을 설정합니다. JSF는 Bean 유효성 검사의 조회 메커니즘 (리플렉션을 사용함)을 사용하여 유효성 검사기를 조회 한 다음 제출 된 값을 유효성 검사기로 전달합니다. 유효성 검사를 통과하면 모델 값이 설정됩니다. 이는 모델의 값을 설정하고 전체 모델에 Bean 유효성 검사를 적용하는 것과는 대조적인데,이 경우'SomeSubClass.someField'의'@ NotEmpty'가 항상 실패합니다. 이 올바른지? – DavidS

+1

실제로 EL은 getters/setters를 통해 항상 모델 값에 액세스하며 절대로 필드를 통해 액세스하지 않습니다. 더욱이, 현장이 반드시 EL을 위해 존재할 필요는 없습니다. – BalusC