PreRenderView постепенно вызывается при каждой обратной передаче

У меня проблема с порядком и количеством казней 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, что обработчик события из представления вызывается перед тем из шаблона и no. 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>

Может кто-нибудь сказать мне причину этих случаев?

2 ответа

Решение

Я могу воспроизвести это. Это вызвано наличием /WEB-INF/beans.xml и неявно таким образом CDI. Это даже происходит, когда вы возвращаетесь к стандартным аннотациям JSF, сохраняя beans.xml файл. Об этом уже сообщается как о проблеме 1771, так и о проблеме 2162. Однако из-за отсутствия конкретного файла WAR, воспроизводящего проблему, и низкого количества голосов, разработчики Mojarra не удосужились взглянуть на это поближе.

Я сообщил об этом еще раз как выпуск 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");
    }

}

Отсутствующий ресурс также может привести к тому, что слушатель будет вызван дважды. Я использую 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 не должен этого делать.

Другие вопросы по тегам