我们在启用JSF 2.2 + PrimeFaces的应用程序中使用了SPA方法。基本思想最初在这里描述得很好:
使用SPA方法在JSF中使用AJAX刷新动态内容
但是,正如我们所知,当使用@ViewScoped bean时,使用这种SPA方法有一个缺点。因为我们实际上总是停留在同一个JSF视图中,所以当我们用新的SPA内容替换面板组的内容时,@ViewScoped bean不会从内存中移除。
我已经找到了一个解决方案,但我想知道这是一个正确的方法,和/或如果有什么遗漏。
基本上,在我们的NavigationService bean(它保存了SPA AJAX请求期间要呈现的页面的名称)中,我们总是执行以下代码:
private void clearViewScopedBeans() {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Iterator<Map.Entry<String, Object>> it = viewMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> entry = it.next();
it.remove();
}
}
这将确保在呈现新的SPA片段之后,所有先前存在的@ViewScoped bean都被删除。
但是,我认为上面的代码只删除了View Scoped bean,而没有删除相关的View States。
我发现了一个旧的博客条目,它似乎做了更多的逻辑:http://javaevangelist.blogspot.sg/2014/08/jsf-21-tip-of-day-clearing-viewscope.html
但我不知道它是否正确。
此外,如果我们想要支持多个窗口选项卡,我们的NavigationService bean(保存当前SPA片段页面名称)也必须是@ViewScoped的,这就引入了一个小问题:当运行上面的代码删除所有现有的@ViewScoped bean…我们必须排除NavigationService bean本身!!否则,我们最终总是加载相同的页面,因为实例化了NavigationService的新实例,使用默认的SPA页面名,而不是新的。
所以,总而言之,我们的代码最终看起来像这样,我们保留了一个"排除"bean名称的Map,我们不想在SPA页面刷新时删除它(即包含SPA页面名称的NavigationService bean)
private void clearViewScopedBeans() {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Iterator<Map.Entry<String, Object>> it = viewMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> entry = it.next();
if(!exclusionViewScopedBeans.contains(entry.getKey())) {
logger.info("Removing an instance of a @ViewScoped bean -> " + entry.getKey());
it.remove();
}
}
}
现在问题是……这是处理这类SPA情况的正确方法吗?我们错过了什么吗?
任何反馈将是非常感激的…
我认为,没有更好的解决方案来删除/清除ViewScopedBeans在SPA(单页应用程序)比你的Maikel:
private void clearViewScopedBeans() {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Iterator<Map.Entry<String, Object>> it = viewMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> entry = it.next();
it.remove();
}
}
我试着找到了很多,但SPA不是"官方推荐"使用JSF的方式,这是一个多页面的应用程序,所以SPA需要更多的摆弄。
或者,Maikel,你找到了或者你用别的方法了吗?