【问题标题】:Why does @PostConstruct callback fire every time even though bean is @ViewScoped? JSF为什么 @PostConstruct 回调每次都会触发,即使 bean 是 @ViewScoped 的? JSF
【发布时间】:2010-05-09 09:45:10
【问题描述】:

我在页面上使用数据表并使用绑定属性将其绑定到我的支持 bean。这是我的代码:-

<?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 回调只触发一次。那为什么每次我使用绑定属性时?我需要绑定属性,并且只想执行一次初始化任务,例如从 web 服务获取数据等。我该怎么办?我应该在哪里写我的初始化任务?

【问题讨论】:

  • 有解决办法吗?同样的问题...

标签: jsf facelets view-scope postconstruct


【解决方案1】:

有趣的是,当您在视图范围的 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 年 12 月):这确实是 JSF2 中的一个错误。这是一个鸡蛋问题。视图范围的 bean 存储在 JSF 视图状态中。因此视图范围的 bean 仅在恢复视图阶段后可用。但是,binding 属性在恢复视图阶段运行,而视图范围的 bean 尚不可用。这会导致创建一个全新的视图范围 bean 实例,然后将其替换为存储在恢复的 JSF 视图状态中的真实视图范围 bean。

这被报告为JSF issue 1492 和JSF spec isssue 787,这将在 JSF 2.2 中得到修复。在此之前,您最好的选择是专门针对请求范围的 bean 使用 binding,或者为特定的功能需求寻找替代方法。


更新(2015 年 3 月):JSF 2.2 修复已向后移植到 Mojarra 2.1.18。因此,如果您仍在使用 JSF 2.0/2.1,您最好至少升级到该版本。另见 a.o. What is component binding in JSF? When it is preferred to be used? 和 JSTL in JSF2 Facelets... makes sense?

【讨论】:

  • JSF 规范没有说明这一点。我已经提交了an issue。顺便说一句,我现在可以重现您的问题,即每次只返回第一项。我在我的环境中犯了一个错误,同一个 bean 已被声明为 faces-config.xml 中的请求范围,它已经覆盖了注释。
  • 嗯! :) 那也是一个错误吗?关于数据表?
  • 查看更新后的答案,DataModel#getRowData() 是更好的选择。
  • 嗨 BalusC,我阅读了您关于 ViewScoped 的文章以及阅读这篇文章。我有两个问题要问你。第一:为什么人们会将表绑定到托管 bean。在这个例子中,我认为他提到了绑定 dataTable 来初始化它的值,但是你可以在@PostConstruct 中初始化一个对象列表,并将它们显示在 dataTable 中。那不会达到同样的结果吗?其次:在您的项目中,您使用了 DataModel,所以如果您将要在 dataTable 中显示的对象列表包装在 DataModel 中,那么无论用户选择哪一行,您都会知道您是否调用 getRowData()?跨度>
  • 1) 这是 JSF 1.x 的遗留物。 2) 是的。只需 getRowData() in action 方法将为您提供调用操作的行。
【解决方案2】:

正如其他人所说,我想说最好的办法是放弃组件绑定(这里不需要它)。

但我要补充一点,您可以通过使用操作参数以更面向对象的方式实现与您尝试相同的效果,如下所示:

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

... 在您的 java 代码中:

  public void action(Item item){
    System.out.println("Clicked!!" + item);
}

【讨论】:

  • 需要注意的是,这需要一个支持 Servlet 3.0 / EL 2.2 的容器。 JSF 2.0 被设计为向后兼容 Servlet 2.5,因此当您在 Servlet 2.5 容器上运行 JSF 2.0 时,这可能会失败。另请参阅this answer 了解所有方式。
  • 我认为它需要 EL 2.2,但不需要 Servlet 3.0。直到上周,我们还在 Glassfish 2 上运行 EL 2.2,它没有 Servlet 3.0,而且运行良好。
【解决方案3】:

如果您有一个 viewscoped bean,并且如果您想保留在表单中输入的值或不想触发 postconstruct,您应该从您的操作方法返回 null。

如果您返回一些结果(例如无效),然后使用 faces-config.xml 将无效结果指向同一页面,则重新创建 viewscoped bean 并因此导致 postconstruct 再次触发。

【讨论】:

    【解决方案4】:

    其他解决方案:

    • 在请求范围 bean 中绑定 HtmlDataTable。
    • 在视图范围 bean 中注入此请求范围 bean。

    JBoss Seam 使用此解决方案将 JSF 组件绑定到会话范围组件。

    【讨论】:

      【解决方案5】:

      balusc 的回答对我帮助很大,我想说我有 mojarra 版本 2.1.7 的错误,我目前使用 2015 年 1 月发布的 2.1.29-01,这个错误已修复,我的问题将 tabview 绑定到 viewscoped bean。使用这个版本,我没有那个错误和绑定,并且 postconstruct 工作正常。 我使用 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

      【讨论】:

        猜你喜欢
        • 2011-02-17
        • 2013-04-27
        • 2011-07-29
        • 1970-01-01
        • 2015-01-25
        • 2014-10-17
        • 2011-02-20
        相关资源
        最近更新 更多