Как и когда удаляется компонент области видимости из кэша LRU в JSF?
Я прочитал отличный ответ, когда боб видовой области разрушен. (см. Как и когда уничтожается компонент @ViewScoped в JSF?), и я автоматически предположил, что уничтоженный компонент также удаляется из кэша области просмотра. Но я мог видеть, что bean-компонент все еще находится в кеше, поэтому я хотел бы знать, следует ли также удалять bean-объект с уничтоженной областью видимости из кэша области действия LRU, если вообще когда-либо?
В нашем приложении мы открываем все детали в отдельных вкладках / окнах. После некоторого открытия / закрытия (зависит от numberOfViewsInSession) мы могли видеть ViewExpiredException в случае, когда первое подробное окно все еще открыто, и пользователь открывал и закрывал другие подробные окна, и через некоторое время он хочет выполнить некоторую операцию в первом окне. Я сделал некоторую отладку, и я вижу, что закрытое представление не было удалено из кэша LRU.
Так это ожидаемое поведение или что-то не так в моем коде? И если это ожидаемое поведение, есть ли какая-нибудь полезная стратегия, как работать с многозадачными / многооконными окнами без большого количества исключений ViewExpiredException, вызванных кешем LRU?. Я знаю, что могу изменить numberOfViewsInSession, но это последний выбор, который я хочу использовать.
Я подготовил простой тестовый пример, и когда я открываю / закрываю файл view.xhtml еще раз, я вижу, что LRUMap растет.
Окружающая среда: JDK7, Моджарра 2.2.4, Tomcat 7.0.47
заранее спасибо
View.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html">
<head>
<title>View Bean</title>
<meta charset="UTF-8"/>
</head>
<body>
<h:form id="viewForm">
<div>#{viewBean.text}</div>
<h:commandButton id="closeButton" value="Close" action="/ClosePage.xhtml"/>
</h:form>
</body>
</html>
index.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Session bean</title>
</h:head>
<h:body>
<h:form id="sessionForm">
<h:outputText value="#{sessionBean.text}"/>
<br/>
<h:link id="linkView" value="Open view.xhmtl" outcome="/view.xhtml" target="_blank"/>
</h:form>
</h:body>
</html>
ClosePage.xhmtl
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head><title>Window will be closed</title></head>
<body>
<script>window.close();</script>
</body>
</html>
ViewBean.java
package com.mycompany.mavenproject2;
import com.sun.faces.util.LRUMap;
import java.io.Serializable;
import java.util.Map;
import javax.annotation.PreDestroy;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
@ManagedBean(name = "viewBean")
@ViewScoped
public class ViewBean implements Serializable {
private static final long serialVersionUID = 13920902390329L;
private int lruMapSize;
/**
* Creates a new instance of ViewBean
*/
public ViewBean() {
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
LRUMap<String, LRUMap> lruMap = (LRUMap) sessionMap.get("com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap");
lruMapSize = lruMap == null ? 0 : lruMap.size();
}
@PreDestroy
void destroyed() {
System.out.println("View bean destroyed");
}
public String getText() {
return "ViewBean LRU cache size:" + Integer.toString(lruMapSize);
}
}
SessionBean.java
package com.mycompany.mavenproject2;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean(name = "sessionBean")
@SessionScoped
public class SessionBean implements Serializable {
private static final long serialVersionUID = 1777489347L;
/**
* Creates a new instance of SessionBean
*/
public SessionBean() {
}
public String getText() {
return "Session bean text";
}
}
1 ответ
Я думаю, что каждый разработчик JSF сталкивается с этим в конце концов. Истинная проблема заключается в том, что вы не можете создать действительно надежную систему с отслеживанием состояния, в которой браузер будет сообщать бину ViewScoped, что это делается со страницей, позволяя бину поддержки уничтожать себя. Вот почему реализации JSF имеют кэш-память LRU для ограничения памяти, используемой сеансом, что является отличным универсальным решением для повседневных приложений.
Есть несколько случаев, когда вы знаете, что вы завершили работу с компонентом ViewScoped, например, перенаправление с этого компонента. Для них вы могли бы написать свой собственный обработчик представления для выполнения более умной системы кэширования, но это не тривиальная задача, и, честно говоря, не стоит затраченных усилий.
Самое простое решение, которое я придумал, - это использовать таймер javascript для выполнения обратной передачи ajax на сервер на каждой странице с компонентом ViewScoped. (Установка этого таймера на выполнение каждые 30 секунд кажется разумной.) Это переместит bean-компонент (и) ViewScoped, связанный со страницей, в нижнюю часть кэша LRU, гарантируя, что их срок действия не истек.
В частности, я использую компонент опроса Primefaces, чтобы извлечь это и вставить шаблон, который будет использоваться всеми компонентами ViewScoped. Размещая этот компонент в его собственной форме, размер запроса остается маленьким.