NullPointerException при попытке доступа к компоненту @EJB в конструкторе управляемого компонента

У меня есть служба EJB.

@Stateless
public class SomeService {}

Я хотел бы ввести это в bean-компонент с областью видимости и инициализировать его:

@ManagedBean
@ViewScoped
public class ViewBean implements Serializable {

    @EJB
    private SomeService someService;

    public ViewBean() {
        System.out.println(someService.getEntity());
    }

}

Однако он выдает следующее исключение:

com.sun.faces.mgbean.ManagedBeanCreationException: Cant instantiate class: com.example.ViewBean.
    at com.sun.faces.mgbean.BeanBuilder.newBeanInstance(BeanBuilder.java:193)
    at com.sun.faces.mgbean.BeanBuilder.build(BeanBuilder.java:102)
    at com.sun.faces.mgbean.BeanManager.createAndPush(BeanManager.java:409)
    at com.sun.faces.mgbean.BeanManager.create(BeanManager.java:269)
    at com.sun.faces.el.ManagedBeanELResolver.resolveBean(ManagedBeanELResolver.java:244)
    at com.sun.faces.el.ManagedBeanELResolver.getValue(ManagedBeanELResolver.java:116)
    at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
    at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
    [snip]
Caused by: java.lang.NullPointerException
    at com.example.ViewBean.<init>(ViewBean.java:42)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at java.lang.Class.newInstance0(Class.java:374)
    at java.lang.Class.newInstance(Class.java:327)
    at com.sun.faces.mgbean.BeanBuilder.newBeanInstance(BeanBuilder.java:188)
    ... 62 more

Чем это вызвано и как я могу это решить?


person ChrisM    schedule 20.05.2013    source источник


Ответы (1)


Другими словами, вы ожидаете, что EJB-инъекция работает скрытно следующим образом:

ViewBean viewBean;
viewBean.someService = new SomeService(); // EJB injected, so that constructor can access it.
viewBean = new ViewBean(); // ViewBean constructed.

Однако это технически невозможно. Невозможно присвоить переменную экземпляра, когда экземпляр вообще не создан.

Канонический подход к выполнению задачи, основанной на введенных зависимостях, сразу после построения заключается в использовании @PostConstruct аннотированный метод.

Итак, чтобы решить вашу конкретную проблему, просто замените

public ViewBean() {

by

@PostConstruct
public void init() { // Note: Method name is fully free to your choice.

Таким образом, процесс под обложками будет примерно следующим:

ViewBean viewBean;
viewBean = new ViewBean(); // ShiftBean constructed.
viewBean.someService = new SomeService(); // EJB injected.
viewBean.init(); // PostConstruct invoked.

Обратите внимание, что конкретная проблема не имеет абсолютно никакого отношения к области просмотра. У вас была бы точно такая же проблема при использовании bean-компонента запроса, сеанса или приложения. Таким образом, это еще одно доказательство того, что вы на самом деле никогда не исключали его как причину, тестируя с использованием другого осциллографа.

person BalusC    schedule 20.05.2013
comment
Спасибо за объяснение, BalusC, PostConstruct сработал, но мне нужно было пометить EJB как переходный, иначе я бы получил java.io.NotSerializableException. Скорее всего, причина в том, что EntityManager не сериализуем. Ранее я пробовал PostConstruct, и при использовании RequestScoped исключений не возникало. Только при использовании ViewScoped вы получите исключение NotSerializableException. - person ChrisM; 21.05.2013
comment
Это не должно было быть обязательным. Видимо, в вашей конструкции что-то более сломано. Какой именно класс упоминается в сообщении NotSerializableException? - person BalusC; 21.05.2013
comment
Спасибо за внимание. Было что-то более сломанное, поэтому я разделил проект, чтобы исследовать, и обнаружил, что причиной является параметр контекста javax.faces.STATE_SAVING_METHOD, установленный на client. У меня была проблема с переполнением буфера ответа (>2 КБ), описанная в stackoverflow.com/a/8072445/1754901, и я использовал предложение # 5, который решил эту проблему, но теперь вызывал исключение NotSerializableException. Поэтому я удалил javax.faces.STATE_SAVING_METHOD и вместо этого вставил предложение № 4 (установленное на 64 КБ). Теперь все исключения ушли и нет необходимости в переходных процессах. - person ChrisM; 21.05.2013
comment
Спасибо BalusC. Компоненты EJB теперь правильно внедряются и могут использоваться во время создания управляемого компонента с помощью PostConstruct. - person ChrisM; 21.05.2013
comment
Метод сохранения состояния не должен иметь значения для EJB, поскольку они внедряются как сериализуемые прокси. Опять же, какой именно класс упоминается в сообщении NotSerializableException? Разве это не только сам управляемый компонент с областью видимости? - person BalusC; 21.05.2013
comment
Я вернул метод сохранения состояния, и исключение возвращается. В трассировке стека мой класс не упоминается. Просто java.io.NotSerializableException: com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate. - person ChrisM; 21.05.2013