Почему обратный вызов @PostConstruct срабатывает каждый раз, даже если bean-компонент @ViewScoped? JSF

Я использую datatable на странице и использую атрибут привязки, чтобы привязать его к моему резервному компоненту. Это мой код: -

<?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"
      xmlns:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
            <h:form prependId="false">

                <h:dataTable var="item" value="#{testBean.stringCollection}" binding="#{testBean.dataTable}">
                    <h:column>
                        <h:outputText value="#{item}"/>
                    </h:column>
                    <h:column>
                        <h:commandButton value="Click" actionListener="#{testBean.action}"/>
                    </h:column>
                </h:dataTable>

            </h:form>

    </h:body>
</html>

Это моя фасоль :-

package managedBeans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.html.HtmlDataTable;

@ManagedBean(name="testBean")
@ViewScoped
public class testBean implements Serializable {

    private List<String> stringCollection;

    public List<String> getStringCollection() {
        return stringCollection;
    }

    public void setStringCollection(List<String> stringCollection) {
        this.stringCollection = stringCollection;
    }

    private HtmlDataTable dataTable;

    public HtmlDataTable getDataTable() {
        return dataTable;
    }

    public void setDataTable(HtmlDataTable dataTable) {
        this.dataTable = dataTable;
    }

    @PostConstruct
    public void init(){
        System.out.println("Post Construct fired!!");
        stringCollection = new ArrayList<String>();
        stringCollection.add("a");
        stringCollection.add("b");
        stringCollection.add("c");

    }

    public void action(){
        System.out.println("Clicked!!");

    }
}

Скажите, пожалуйста, почему @PostConstruct срабатывает каждый раз, когда я нажимаю кнопку? Он должен срабатывать только один раз, пока я нахожусь на той же странице, потому что мой bean-компонент @ViewScoped. Кроме того, если я удалю атрибут привязки, все будет работать нормально, а обратный вызов @PostConstruct сработает только один раз. Тогда почему каждый раз, когда я использую атрибут привязки? Мне нужен атрибут привязки, и я хочу выполнять задачи инициализации, такие как получение данных из веб-службы и т. д., только один раз. Что я должен делать? Где я должен написать свою задачу инициализации?


person TCM    schedule 09.05.2010    source источник
comment
Есть ли решение для этого? вот такая же проблема...   -  person Julio Faerman    schedule 15.07.2011


Ответы (5)


Интересно, что когда вы используете привязку компонента к bean-компоненту с областью представления, область представления прерывается.

Я не уверен, что это ошибка в JSF2, мне нужно сначала прочитать всю спецификацию JSF2. На данный момент лучше всего отказаться от привязки компонента и передать выбранный элемент с помощью нового синтаксиса аргумента метода EL 2.2:

<h:dataTable var="item" value="#{testBean.stringCollection}">
    <h:column>
        <h:outputText value="#{item}"/>
    </h:column>
    <h:column>
        <h:commandButton value="Click" action="#{testBean.action(item)}"/>
    </h:column>
</h:dataTable>

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


Обновление (декабрь 2012 г.): это действительно ошибка в JSF2. Это проблема куриного яйца. Компоненты области представления хранятся в состоянии представления JSF. Таким образом, bean-компоненты с областью просмотра доступны только после фазы восстановления просмотра. Однако атрибут binding запускается на этапе восстановления представления, в то время как bean-компоненты с областью представления еще недоступны. Это приводит к созданию совершенно нового экземпляра компонента с областью действия представления, который затем позже заменяется компонентом с областью действия реального представления, который был сохранен в восстановленном состоянии представления JSF.

Об этом сообщается как проблема JSF 1492 и выпуск спецификации JSF 787, который будет исправлен для JSF 2.2. До тех пор вам лучше всего использовать только binding по запросу bean-компоненты с областью действия или искать альтернативные способы для конкретных функциональных требований.


Обновление (март 2015 г.): исправление JSF 2.2 было перенесено в Mojarra 2.1.18. Поэтому, если вы все еще используете JSF 2.0/2.1, вам лучше обновиться хотя бы до этой версии. См. также Что такое привязка компонентов в JSF? Когда предпочтительнее использовать? и JSTL в JSF2 Facelets... имеет смысл?

person BalusC    schedule 09.05.2010
comment
Спецификация JSF ничего не говорит об этом. Я зарегистрировал проблему. Кстати, теперь я мог воспроизвести вашу проблему, когда каждый раз возвращался только 1-й элемент. Я допустил ошибку в своей среде, тот же самый bean-компонент уже был объявлен как область запроса в Faces-config.xml, что переопределило аннотации. - person BalusC; 10.05.2010
comment
Хм! :) так это тоже ошибка? о данных? - person TCM; 10.05.2010
comment
Смотрите обновленный ответ, DataModel#getRowData() - лучший выбор. - person BalusC; 06.06.2010
comment
Пожалуйста. На самом деле, спасибо за вдохновение для ведения блога :) - person BalusC; 06.06.2010
comment
Привет, BalusC, я прочитал вашу статью о ViewScoped, а также прочитал эти сообщения. У меня к вам два вопроса. Во-первых: зачем людям привязывать таблицу к управляемому компоненту. В этом примере, я думаю, он упомянул о привязке dataTable для инициализации его значения, но вы можете просто инициализировать список объектов в @PostConstruct и отображать их в dataTable. Разве это не приведет к тому же результату? Во-вторых: в вашем проекте вы использовали DataModel, поэтому, если вы переносите список объектов, которые вы хотите отобразить в dataTable, в DataModel, то какую бы строку ни выбрал пользователь, вы узнаете, вызовете ли вы getRowData()? - person Thang Pham; 13.10.2010
comment
1) Это остаток от JSF 1.x. 2) Да. Просто getRowData() в методе действия вы получите строку, в которой было вызвано действие. - person BalusC; 13.10.2010
comment
Привет, балус, какие-нибудь решения проблемы в viewScope ??, спасибо ... извините за мой английский. - person Marco R; 25.01.2013

Как сказал другой, я бы сказал, что лучше всего отказаться от привязки компонентов (здесь вам это не нужно).

Но я бы добавил, что вы можете добиться того же, что вы пытаетесь сделать более объектно-ориентированным способом, используя параметры действия, например:

<h:commandButton value="Click" action="#{testBean.action(item)}"/>

... и в вашем Java-коде:

  public void action(Item item){
    System.out.println("Clicked!!" + item);
}
person ymajoros    schedule 16.03.2011
comment
Следует отметить, что для этого требуется контейнер с поддержкой Servlet 3.0/EL 2.2. JSF 2.0 спроектирован так, чтобы быть обратно совместимым с Servlet 2.5, поэтому это может привести к сбою при запуске JSF 2.0 в контейнере Servlet 2.5. См. также этот ответ для всех способов. - person BalusC; 16.03.2011
comment
Я думаю, что для этого требуется EL 2.2, но не Servlet 3.0. До прошлой недели мы запускали EL 2.2 на Glassfish 2, в котором нет Servlet 3.0, и он прекрасно работал. - person ymajoros; 23.03.2011

Если у вас есть bean-компонент с областью просмотра и если вы хотите сохранить значения, которые были введены в форму, или не хотите запускать postconstruct, вы должны вернуть null из своего метода действия.

Если вы вернете некоторый результат (например, недействительный), а затем укажете неверный результат на ту же страницу с помощью Faces-config.xml, тогда bean-компонент viewscope будет воссоздан и, таким образом, вызовет повторный запуск postconstruct.

person Pramod Kankure    schedule 26.03.2013

Другое решение:

  • Связывание HtmlDataTable в bean-компоненте области запроса.
  • Вставьте этот компонент области запроса в компонент области представления.

JBoss Seam использует это решение для привязки компонентов JSF к компоненту области диалога.

person Juan Miguel Bernal González    schedule 16.05.2013

Ответ balusc мне очень помог, я хотел бы сказать, что у меня была эта ошибка с mojarra версии 2.1.7, в настоящее время я использую 2.1.29-01, выпущенную в январе 2015 года, и эта ошибка исправлена, моя проблема заключалась в привязке tabview для bean-компонента с областью видимости. В этой версии у меня нет этой ошибки, а привязка и постконструкция работают нормально. Я использую Jboss 5.2, и мне приходится использовать mojarra 2.1.x, поэтому я надеюсь, что этот ответ поможет другим людям в такой же ситуации.

http://mvnrepository.com/artifact/com.sun.faces/jsf-api/2.1.29-01 http://mvnrepository.com/artifact/com.sun.faces/jsf-impl/2.1.29-01

person Luis Vidal    schedule 20.03.2015