Использование JSTL/ основных тегов портит ViewScoped Bean
Я думаю, что я столкнулся с ошибкой в Mojarra 2.1.0. Может быть, я что-то пропустил, но будь я проклят, если смогу это увидеть.
Я полагаюсь на множество bean-компонентов @ViewScoped для сохранения состояния, в то время как браузер выполняет AJAX на сервере. Я нахожу, что когда я использую определенные теги, бин @ViewScoped начинает восстанавливаться, когда это не должно быть. Вот мой тестовый бэк:
/*
* TestStuff.java
*/
package test;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
/**
* Backing bean for test.xhtml -- working out AJAX/SVG connection
*
*/
@ManagedBean
@ViewScoped
public class TestStuff implements Serializable {
private int counter = 0;
public TestStuff() {
log("TestStuff(): {0}", this);
}
public String getRandomNumber() {
int i = (int) (Math.random() * 1000000.0);
return String.format("%d", i);
}
public int getCounter() { return counter; }
public List<String> getStuff() {
return Arrays.asList("big", "bad", "wolf");
}
public void pushButton(ActionEvent evt) {
log("TestStuff.pushButton({0}): {1}",
new Object[] { evt, ++counter });
}
}
А вот страница JSF Facelets, которая ее использует:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
<title>Test Page</title>
</h:head>
<h:body>
<h1>Test Page</h1>
<p>If you are reading this text, the server
is not properly configured.</p>
<ui:composition id="compRoot" template="/template5.xhtml">
<ui:define name="content">
<h1>Test</h1>
<h:form id="formTest">
<p:commandButton value="Do Me"
actionListener="#{testStuff.pushButton}"
update="testUpdate" />
<p:panel id="testUpdate" >
<h:outputText value="Random output is: " />
<h:outputText
value="#{testStuff.randomNumber}"
/>
<h:outputText value=" Counter is: "/>
<h:outputText
value="#{testStuff.counter}"
/>
</p:panel>
<h:panelGrid columns="5" border="1" >
<c:forEach items="#{testStuff.stuff}" var="x">
<h:outputText value="#{x}" />
</c:forEach>
</h:panelGrid>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
Так вот что идет не так. Когда вы нажимаете командную кнопку "Do Me", каждый раз создается новый экземпляр компонента поддержки, как если бы это был компонент @RequestScoped. Я могу видеть это через вызов log() в конструкторе.
Если вы измените bean-компонент на @SessionScoped, этого не произойдет. Вы получаете один экземпляр компонента независимо от того, сколько раз нажата кнопка.
ОДНАКО - если вы оставите его как @ViewScoped и извлечете элемент c:foreach и его содержимое, теперь он больше не будет создавать экземпляр компонента каждый щелчок. Другими словами это теперь работает как ожидалось.
Это ошибка мохарры или я здесь что-то не так делаю?
1 ответ
Это известная "ошибка": выпуск 1665. Это проблема куриного яйца в отношении частичного сохранения состояния.
В вашем случае, однако, вы также можете просто использовать <ui:repeat>
,
<ui:repeat value="#{testStuff.stuff}" var="x">
<h:outputText value="#{x}" />
</ui:repeat>
Лучше всего избегать использования тегов JSTL при использовании @ViewScoped
, Единственная альтернатива - отключить частичное сохранение состояния с помощью параметра контекста в web.xml
:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
Но это делает взгляды более запоминающимися.