当查看作用域的bean被破坏时,我读了一个很好的答案。(请参见如何在JSF中销毁@ViewScoped bean以及何时销毁?),我自动假定销毁的bean也将从视图范围缓存中删除。但是我可以看到bean仍在高速缓存中,所以我想知道是否也应该从LRU视图范围高速缓存中删除被破坏的view作用域bean。
在我们的应用程序中,我们在sepeare选项卡/窗口中打开所有详细信息。经过一些打开/关闭(取决于numberOfViewsInSession)之后,我们可以看到ViewExpiredException,以防第一个详细信息窗口仍处于打开状态,并且用户一直在打开和关闭另一个详细信息窗口,并且过了一段时间后,他想在第一个窗口中进行一些操作。我已经进行了一些调试,并且可以看到没有从LRU缓存中删除关闭的视图。
那么这是预期的行为还是我的代码有问题?并且,如果这是预期的行为,那么有没有什么有用的策略可以在没有很多由LRU缓存引起的ViewExpiredException的情况下使用multitabs / multiwindow?我知道我可以更改numberOfViewsInSession,但这是我要使用的最后选择。
我准备了一个简单的测试用例,当我多次打开/关闭view.xhtml时,可以看到LRUMap正在增长。
环境:JDK7,mojarra 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";
}
}
我认为最终每个JSF开发人员都会遇到这种情况。真正的问题在于您无法设计出一种真正可靠的有状态系统,在该系统中,浏览器将向ViewScoped bean发出信号,表明该页面已完成,从而使backing bean自行销毁。这就是为什么JSF实现具有LRU缓存来限制会话使用的内存的原因,这对于日常应用程序来说是一个很好的通用解决方案。
在某些情况下,您知道已经完成了ViewScoped bean,例如从该bean进行重定向。对于这些,您可以编写自己的视图处理程序以执行更智能的缓存系统,但这并不是一件容易的事,坦率地说,这并不值得。
我想到的最简单的解决方案是使用JavaScript计时器,使用ViewScoped bean在每个页面上执行Ajax回发到服务器的操作。(将此计时器设置为每30秒执行一次似乎很合理。)这会将与页面关联的ViewScoped bean移至LRU缓存的底部,以确保它们没有过期。
特别是,我使用了primefaces民意测验组件来实现这一点,并将其粘贴在一个模板中,以供所有ViewScoped bean使用。通过以自己的形式放置此组件,请求大小将保持很小。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句