PreRenderView постепенно вызывается при каждой обратной передаче
У меня проблема с порядком и количеством казней f:event type="preRenderView"
,
Во время моего поиска здесь я нашел, как обычно, ответы от BalusC в этом и этом посте, связанном с моей проблемой - тем не менее он оставляет мне два вопроса:
Когда я ставлю один
f:event type="preRenderView"
в файле шаблона (для управления общими задачами, такими как проверки состояния пользователя, которые применяются ко всем моим представлениям) и другимf:event type="preRenderView"
в каждом представлении (для обработки определенных инициализаций представления) мне интересно, почему метод слушателя из представления вызывается перед тем из шаблона.Когда я ставлю весь
<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 не должен этого делать.