2015-01-19 2 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> 

И у меня есть этот защитный компонент:

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 имеет такое же имя, как и частные переменные в родительском классе. Это не имеет значения, потому что вы не можете затенять частное поле.

Две вещи, чтобы отметить:

  1. Если запустить метод main в BackingBean, валидатор будет всегда возвращать ошибку проверки (сообщение «не может быть пустым»), потому что SomeSubClass.someField всегда нуль. Такое поведение кажется мне правильным.
  2. Если вы отправите форму с пустым значением, вы получите сообщение об ошибке «может не быть пустым»; если вы введете значение, оно пройдет проверку, но вы увидите на консоли, что значение SomeSubClass.someField по-прежнему равно нулю. Такое поведение кажется мне неправильным.
    • Если вы изменили название SomeSubClass.someField на someField2, вы можете отправить форму с пустым значением. Такое поведение кажется мне неправильным.

Оказывается, что проверка фазы JSF и Hibernate Validator не согласен на такое поведение. JSF применяет валидатор @NotEmpty частного поля в подклассе к полю с тем же именем в родительском классе, но Hibernate Validator не показывает это поведение при тестировании изолированно.

Может ли кто-нибудь объяснить это поведение? Это ошибка или недоразумение в моих собственных ожиданиях?

Использование:

  • GlassFish сервер Open Source Edition 3.1.2.2
  • Mojarra 2.1.6
  • Hibernate Validator 4.3.0.Final
+0

@BalusC, спасибо, что посмотрели. После вашего комментария, я тестировал Hibernate Validator в изоляции и обнаружил, что это поведение является правильным, насколько мне известно. Я добавил сравнение фазы проверки JSF с Hibernate Validator на мой вопрос, который демонстрирует проблему. – DavidS

+0

Я вижу. Хорошее обновление! – BalusC

ответ

1

JSF использует EL получить/комплект модельные значения соответствуют правилам Джавабе. EL использует рефлексию для проверки и использования публичных геттеров и сеттеров. Другими словами, #{backingBean.someClass.someField} фактически использует getSomeField() и setSomeField() для получения/установки модели/представленного значения. Управляемое поле фактически является тем, которое находится в SomeClass.

Проверка боба использует отражение для проверки полей и игнорирует присутствие публичных геттеров/сеттеров. Другими словами, BV #{backingBean.someClass.someField} фактически имеет место на SomeSubClass.

Это все объясняет. Но я согласен, что это сбивает с толку и кажется «неправильным». Он будет работать так, как ожидалось, когда вы также переопределите геттер и сеттер в 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; 
    } 
} 

Этот способ EL будет видеть его и использовать его в качестве значения модели.

Это, однако, неудобно (@Override, который просто вызывает super, будет использовать инструменты для анализа статического кода, такие как Sonar, PMD, Findbugs и т. Д.). Лучшей альтернативой является переход @NotEmpty к перегруженной добытчика:

public static class SomeSubClass extends SomeClass { 

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

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

BV также поддерживает эту конструкцию, поэтому он будет оставаться работать.

+0

Позвольте мне посмотреть, понимаю ли я. _Используя параметры модели__, JSF просматривает Validator с помощью механизма поиска Bean Validation (который использует отражение), а затем JSF передает переданное значение в Validator. Если он проходит проверку, устанавливаются значения модели. Это контрастирует с установкой значений на модели, а затем применением проверки Bean для всей модели, и в этом случае '@ NotEmpty' на' SomeSubClass.someField' всегда терпит неудачу. Это верно? – DavidS

+1

Действительно, EL всегда обращается к значениям модели через геттеры/сеттеры, никогда не используя поля. Более того, для EL не обязательно должно существовать поле. – BalusC