Как добавить в @FacesValidator с помощью @EJB, @PersistenceContext, @Inject, @Autowired

Как я могу ввести зависимость как @EJB, @PersistenceContext, @Inject, @AutoWiredи т. д. в @FacesValidator? В моем конкретном случае мне нужно внедрить управляемый компонент Spring через @AutoWired:

@FacesValidator("emailExistValidator")
public class EmailExistValidator implements Validator {

    @Autowired
    private UserDao userDao;

    // ...
}

Однако, это не было введено, и это остается null, в результате чего java.lang.NullPointerException, Кажется, что @EJB, @PersistenceContext а также @Inject тоже не работает.

Как ввести сервисную зависимость в мой валидатор, чтобы я мог получить доступ к БД?

1 ответ

Решение

@FacesValidator не управляется контейнером для инъекций. Вы должны сделать это управляемым бобом. Используйте Spring's @Component CDI @Named или JSF @ManagedBean вместо @FacesValidator для того, чтобы сделать его управляемым компонентом и таким образом иметь право на внедрение зависимостей.

Например, если вы хотите использовать JSF @ManagedBean:

@ManagedBean
@RequestScoped
public class EmailExistValidator implements Validator {
    // ...
}

Вы также должны ссылаться на него как на управляемый компонент #{name} в EL вместо идентификатора валидатора в жестко закодированной строке. Таким образом, так

<h:inputText ... validator="#{emailExistValidator.validate}" />

или же

<f:validator binding="#{emailExistValidator}" />

вместо

<h:inputText ... validator="emailExistValidator" />

или же

<f:validator validatorId="emailExistValidator" />

Это действительно неловко. Ребята из JSF подтвердили эту неловкую оплошность, и они сделают @FacesValidator (а также @FacesConverter) приемлемая цель инъекции в предстоящем JSF 2,2 2.3, см. Также выпуск спецификации JSF 763. Для EJB есть обходной путь, вручную извлекая его из JNDI, см. Также Получение @EJB в @FacesConverter и @FacesValidator. Если вам случится использовать расширение CDI MyFaces CODI, то вы также можете решить его, поставив @Advanced аннотация по классу.

Смотрите также:


Обновление: если вы используете библиотеку утилит JSF OmniFaces, начиная с версии 1.6 добавлена ​​прозрачная поддержка использования @Inject а также @EJB в @FacesValidator Класс без каких-либо дополнительных настроек или аннотаций. Смотрите также CDI @FacesValidator пример витрины.

Теперь вы можете вставлять в валидаторы JSF, если используете Java EE 8 и / или JSF 2.3.

Протестировано с использованием Mojarra 2.3.9.payara-p2 на Payara Server 5.192 #badassfish.

<?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://xmlns.jcp.org/jsf/html">
    <h:body>
        Hello from Facelets
        <h:form>
            <h:messages/>
            <h:inputText value="#{someBean.txtField}" validator="someValidator"/>
        </h:form>
    </h:body>
</html>
import javax.inject.Named;
import javax.enterprise.context.Dependent;

@Named(value = "someBean")
@Dependent
public class SomeBean {

  private String txtField;

  public String getTxtField() {
    return txtField;
  }

  public void setTxtField(String txtField) {
    this.txtField = txtField;
  }
}
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import javax.inject.Inject;

@FacesValidator(value = "someValidator", managed = true)
public class CustomValidator implements Validator<String> {

  @Inject
  NewClass newClass;

  @Override
  public void validate(FacesContext context, UIComponent component, String value)
      throws ValidatorException {

    System.out.println("validator running");
    System.out.println("injected bean: " + newClass);

    if (value != null && value.equals("badvalue")) {
      throw new ValidatorException(new FacesMessage(newClass.getMessage()));
    }
  }
}
public class NewClass {

  public String getMessage() {
    return "secret message";
  }
}
import javax.faces.annotation.FacesConfig;

// WITHOUT THIS INJECTION WILL NOT WORK!
@FacesConfig(version = FacesConfig.Version.JSF_2_3)
public class ConfigurationBean {
}

Должно отображаться что-то вроде:

Я бился головой о стену около часа, прежде чем осознал необходимость ConfigurationBean. Из документации:

FacesConfig.Version.JSF_2_3 Это значение указывает, что CDI следует использовать для разрешения EL, а также для включения JSF CDI-инъекции, как указано в Разделе 5.6.3 "CDI для разрешения EL" и Разделе 5.9 "Интеграция CDI"

И из этой проблемы GitHub https://github.com/eclipse-ee4j/glassfish/issues/22094:

По умолчанию JSF 2.3 работает в режиме совместимости с предыдущими выпусками JSF, если управляемый компонент CDI не включен в приложение с аннотацией @javax.faces.annotation.FacesConfig. Чтобы переключиться в режим JSF 2.3, вам понадобится компонент конфигурации, как показано ниже: (показывает ConfigurationBean)

...

Тот факт, что JSF необходимо переключить на "текущую версию", был весьма спорным. Практически вся группа EG проголосовала против этого, но в конечном итоге мы не смогли обойти требования обратной совместимости, которые JCP устанавливает для Java EE и руководствуется спецификациями.

Другие вопросы по тегам