6

我对f:event type="preRenderView".

在我在这里搜索的过程中,我发现了 BalusC 在这个这篇与我的问题相关的帖子中的常见答案 - 仍然给我留下了两个问题:

  1. 当我将一个f:event type="preRenderView"放在模板文件中(用于管理常见任务,例如检查适用于我所有视图的用户状态)和f:event type="preRenderView"每个视图中的另一个(用于处理视图特定的初始化)时,我想知道为什么调用视图中的侦听器方法在模板中的那个之前。

  2. 当我按照建议将整个放在<f:metadata><f:event [..] /></f:metadata>后面时,它会在从登录页面重定向到该页面后被调用两次,但是当我在它只被调用一次ui:define之后将它提高一级时。ui:composition


更新:示例

以下示例演示了上述行为:

这是模板文件template_test.xhtml,包含一个preRenderView事件侦听器,该侦听器在所有视图的处理程序中调用一个通用方法:

<?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 xml:lang="de" lang="de" xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    <link rel="stylesheet" type="text/css" href="../resources/css/style.css" />
</h:head>
<h:body>
    <f:event type="preRenderView" listener="#{testHandler.initCommon()}" />
    <div id="content">
        <ui:insert name="content" />
    </div>
</h:body>
</html>

这是视图文件test.xhtml,还包含一个事件侦听器,用于preRenderView在处理程序中调用视图特定方法和通过处理程序方法重定向的命令按钮:

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    template="template_test.xhtml">
    <ui:define name="content">
        <f:metadata>
            <f:event type="preRenderView"
                listener="#{testHandler.initIndividual()}"></f:event>
        </f:metadata>
        <h:form>
            <h:commandButton value="Redirect" action="#{testHandler.redirect()}" />
        </h:form>
    </ui:define>
</ui:composition>

这是TestHandler.java包含 3 个方法的处理程序类:

package test;

import java.io.Serializable;

import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named
@SessionScoped
public class TestHandler implements Serializable {

    private static final long serialVersionUID = -2785693292020668741L;

    public void initCommon() {
        System.out.println("Common init called.");
    }

    public void initIndividual() {
        System.out.println("Init for individual page called.");
    }

    public String redirect() {
        return "test/test.xhtml?faces-redirect=true";
    }
}

现在,这是我在请求测试页面时在我的 tomcat 日志中看到的内容:

Init for individual page called.
Common init called.
Init for individual page called.

这表明没有。1,视图中的事件处理程序在模板中的事件处理程序之前被调用,并且没有。2,从视图中调用事件处理程序两次。

它还显示了第三点(这就是为什么我包含一个带有重定向到同一页面的按钮的原因)显示如果页面被重定向请求会发生什么 - 单个页面被调用更多次:

Init for individual page called.
Common init called.
Init for individual page called.
Init for individual page called.

两者都没有。2 和 3 可以通过将整个元数据部分放在上面ui:define或通过向视图的元数据部分添加一个虚拟参数来防止,该参数不包含在 URL 中:

<f:metadata>
    <f:viewParam name="dummyToDenySecondRedirect" />
    <f:event type="preRenderView"
        listener="#{testHandler.initIndividual()}"></f:event>
</f:metadata>

有人能告诉我这些案件的原因吗?

4

2 回答 2

5

我可以重现它。这是由 CDI 的存在/WEB-INF/beans.xml和隐含的 CDI 引起的。当您在保留beans.xml文件的同时切换回标准 JSF 注释时,甚至会发生这种情况。这已经报告为issue 1771issue 2162。然而,由于没有具体的 WAR 文件重现该问题并且投票率低,Mojarra 开发人员没有费心仔细研究它。

我已将其再次报告为issue 2719。这个问题可以用一个更小的例子来重现:

<!DOCTYPE html>
<html lang="en"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
>
    <f:metadata>
        <f:event type="preRenderView" listener="#{bean.preRenderView}" />
    </f:metadata>
    <h:head>
        <title>preRenderView fail</title>
    </h:head>
    <h:body>
        <p>On initial request, you'll see the listener being invoked twice instead of only once.</p>
        <p>On every postback by the below button, you'll see the listener being invoked once more.</p>
        <h:form>
            <h:commandButton value="submit" />
        </h:form>
        <p>Note however that this increments every time you issue the postback.</p>
        <p>If you remove <code>/WEB-INF/beans.xml</code> and redeploy, then the issue will disappear.</p>
    </h:body>
</html>

package com.example;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Bean {

    public void preRenderView() {
        System.out.println("preRenderView called");
    }

}
于 2013-02-01T13:22:18.887 回答
1

缺少的资源也可能导致监听器被调用两次。我在这里使用 MyFaces 2.2:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets">

<f:metadata>
    <f:event type="preRenderView" listener="#{testController.update()}" />
</f:metadata>

<h:head>
    <script type="text/javascript" src="#{resource['/js/missing.js']}" />
    <title>Test</title>
</h:head>

<h:body>
    <p>TestController.update() is called twice.</p>
</h:body>

</html>

显然,您首先不应该包含任何缺失或过时的资源。但是,假设您(错误地)并且您的听众被连续两次调用,您没有机会找到该问题的实际原因。JSF 不应该这样做。

于 2014-12-06T12:37:54.110 回答