【问题标题】:javax.faces.view.ViewScoped bean and multiple tabs issuejavax.faces.view.ViewScoped bean 和多个选项卡问题
【发布时间】:2019-02-27 20:31:04
【问题描述】:

在 JSF 应用程序(基于 Payara 5.183)中,我使用如下模式在某些操作后重定向用户:

@Named
@ViewScoped
public class ModelViewBean implements Serializable {
    private Model _model;
    ...
    public String delete() {
        System.out.println(">> Deleting model with ID: " + _model.getId());
        _appDaoBean.daoDelete(_model);
        return "/main.xhtml?faces-redirect=true";
    }
    ...
}

问题:如果有两个或多个页面使用不同的 _model 对象打开 - 操作 delete() 会导致第一次执行后其他页面上的 _model.getId() 出现 NPE。

同时像下面这样的方法工作正常:

@Named
@ViewScoped
public class ModelViewBean implements Serializable {
    private Model _model;
    ...
    public void delete() {
        System.out.println(">> Deleting model with ID: " + _model.getId());
        _appDaoBean.daoDelete(_model);
        FacesContext.getCurrentInstance().getExternalContext().redirect("/main.xhtml");
    }
    ...
}

我已经记录了30sec video with the issue

也是样例项目is published on the GitHub

NPE 的原因是什么,在这种情况下,在某些操作后使用导航最合适的方式是什么?

谢谢!

附注What is the difference between redirect and navigation/forward and when to use what? 之类的主题我已经阅读过,但到目前为止还没有找到我的问题的答案。

更新 1:

main.xhtml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">

    <h:body>
        <h:form id="f1">

            <h2>Main Page</h2>

            <p:link outcome="/model.xhtml" value="Model1">
                <f:param name="id" value="1"/>
            </p:link>

            <br/>

            <p:link outcome="/model.xhtml" value="Model2">
                <f:param name="id" value="2"/>
            </p:link>

        </h:form>
    </h:body>
</html>

model.xhtml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <f:view>
        <f:metadata>
            <f:viewParam name="id" value="#{modelViewBean.id}" />
            <f:viewAction action="#{modelViewBean.initModel}" />
        </f:metadata>
        <h:body>
            <h:form id="f1">

                <p:outputLabel value="Model ID: #{modelViewBean.model}" />
                <br/>
                <p:commandButton value="Delete" action="#{modelViewBean.delete()}" process="@this" update="@form" />

            </h:form>
        </h:body>
    </f:view>
</html>

ModelViewBean.java

....
import javax.faces.view.ViewScoped;
....

@Named
@ViewScoped
public class ModelViewBean implements Serializable {

    private static final long serialVersionUID = 6400111954793903238L;

    private String _id;
    private String _model;
    private Date _beanCreateTime;


    @PostConstruct
    private void init() {
        System.out.println(">> @PostConstruct -> init()");
        _beanCreateTime = new Date();
    }


    public String initModel() {
        System.out.println(">> ViewAction -> initModel()");
        if (_id == null || _id.trim().isEmpty()) {
            return "/main.xhtml?faces-redirect=true";
        }
        _model = UUID.randomUUID().toString();
        return null;
    }


    public String delete() {
        System.out.println(">> Deleting model with ID: " + _model.toUpperCase());
        return "/main.xhtml?faces-redirect=true";
    }


    public String getId() {
        return _id;
    }


    public void setId(String id) {
        this._id = id;
    }


    public String getModel() {
        return _model;
    }


    public Date getBeanCreateTime() {
        return _beanCreateTime;
    }
}

NPE:

java.lang.NullPointerException
at local.jsfsample.ModelViewBean.delete(ModelViewBean.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
....

重现问题的步骤:

  1. 打开 main.xhtml
  2. 同时打开两个选项卡(Model1 和 Model2)中的两个链接
  3. 使用 Model1 在第一个选项卡中按“删除”按钮 - 一切正常
  4. 在 Model2 的第二个选项卡中按“删除”按钮 - NPE 为 _model 等于 null(我还注意到 @PostConstruct 也被触发)

更新 2:

已更新主题以反映问题的更根本原因。感谢 cmets 中指向类似帖子的链接。

【问题讨论】:

  • JSF 2.4 不作为正式版本存在。将所有 Java EE 代码移至 Eclipse 是一种奇怪的(Oracle 要求的)副作用。示例代码(minimal reproducible example 风格,应内联发布。请使用 NPE 发布堆栈跟踪,并明确提及 NPE 发生在哪一行...
  • 而且,您是否确实暗示当 bean 处于不同范围时不会发生 NPE?
  • @BalusC 谢谢你的提问!根据使用场景 - 我不能使用任何其他范围,因为我需要同时打开两个具有不同模型(实体)的相似页面。因此,尝试解决使用 @ViewScoped bean - 并没有真正帮助(即使它可以正常工作)。
  • @Kukeltje 感谢您抽出宝贵时间查看我的帖子!我已经发布了包含问题根本原因的确切代码。 GitHub 上的链接提供了完整的示例项目代码。 Stack-trace 很简单,没什么特别的。此外,通过查看我附加的短视频,问题本身变得非常自我解释。再次感谢您的宝贵时间!附言该问题也可以在带有 JSF 2.3.2 的 Glassfish 5 上重现。
  • 尝试与其他范围。它在那里也失败了,你的标题不正确,人们可能会开始解决错误的方向。请再次发布minimal reproducible example inline

标签: jsf redirect nullpointerexception


【解决方案1】:

从 Wildfly 11 -> Wildfly 14 (Mojarra 2.2.13.SP4 -> 2.3.5.SP2) 迁移后,我也遇到了这个问题

此演示项目适用于 Wildfly 11,但在 Wildfly 14 上失败。

看起来 ViewMap 中的引用被删除了,但实际的 ViewScoped bean 没有被破坏。

我已经打开了https://issues.jboss.org/browse/WFLY-11275,希望能找到它。

【讨论】:

  • 我喜欢@org.omnifaces.cdi.ViewScoped 的实现方式。你也可以试试看。
  • 当我查看答案中的链接时,这似乎已在 WildFly 16 中得到修复。他们将 Mojarra 2.3.9 的修复程序反向移植到 WildFly 使用的 2.3.5SPx 版本。
  • 在这一点上,我不相信这个问题在主要的 Mojarra 中得到了解决。它在 Payara 中得到修复,然后同样的修复被用于为 Wildfly 16.0.0.Beta1 创建一个补丁版本。 Mojarra 的问题仍然悬而未决。 github.com/eclipse-ee4j/mojarra/issues/4509
猜你喜欢
  • 2012-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 2016-02-05
  • 2012-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多