【问题标题】:Removing @ViewScoped beans in a SPA JSF 2.2 application在 SPA JSF 2.2 应用程序中删除 @ViewScoped bean
【发布时间】:2016-12-25 02:28:21
【问题描述】:

我们在启用 JSF 2.2 + PrimeFaces 的应用程序中使用 SPA 方法。 最初在这里很好地描述了基本思想:

Refreshing dynamic content with AJAX in JSF using SPA approach

但是,正如我们所知,在使用 @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 sn-p 后,删除所有先前存在的 @ViewScoped bean。

但是,我认为上面的代码只删除了 View Scoped bean,而不是相关的 View States。对吗?

我发现了一个旧的博客条目,它似乎做了更多的逻辑: http://javaevangelist.blogspot.sg/2014/08/jsf-21-tip-of-day-clearing-viewscope.html

但我也不知道它是否正确。

另外,如果我们想支持多窗口标签,我们的NavigationService bean,持有当前SPA sn-p页面名称,也必须是@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 情况的正确方法吗?我们在这里遗漏了什么吗?

任何反馈都将不胜感激......提前非常感谢!

【问题讨论】:

  • 这就是我使用的实现——就正确的方法而言,我得到的印象是你应该在 JSF 中避免这种事情,但现实是有时你需要这样做
  • 为什么不使用对话范围的 bean?
  • 因为这个特定的项目使用 Spring IoC 而不是 CDI...所以到目前为止还没有内置的 Conversation 范围。 :( 无论如何,我也在考虑创建可以实现相同功能的新(Spring)范围。到目前为止,我已经成功地为 PrimeFaces TabView 组件创建了一个 TabScope。每个选项卡都可以有自己的 JSF bean 范围,在构建选项卡时创建,并在关闭选项卡时销毁。我想我可以使用相同的方法来开发更通用的对话范围 ...

标签: jsf primefaces cdi single-page-application view-scope


【解决方案1】:

我认为,在 SPA(单页应用程序)中删除/清除 ViewScopedBeans 没有比您的 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,你有没有找到或者你用别的方法?

【讨论】:

  • SPA 也不是不推荐的方式......您只是不能使用与特定视图相关的功能。您可以使用您可以更轻松地控制而不是“滥用”viewscoped bean 的其他范围 bean(想到对话)。你可以读到他是这样解决的
  • 嗨 Radek 和 Kukeltje。到目前为止,就服务器端而言,这是我们正在使用的方法,并且工作正常。最后最大的问题是在客户端,因为许多 PrimeFaces jQuery UI 小部件没有完全准备好 SPA,导致大量内存泄漏。在移动到新视图之前,我们必须遍历每个 SPA 页面上的所有 PF 小部件,并手动取消绑定特定 PF 小部件的事件,以及其他一些棘手的场景(禁用轮询、空闲计时器、键绑定......)。但到目前为止,我可以说我们正在使用带有 PrimeFaces 和 SPA 方法的 JSF 2.2,现在没有大问题。
  • 嗨 Maikel(和 Kukeltje)——非常感谢您的经验和选择。我很高兴看到,这种 SPA 方法在现实世界中是有效的。我们已经开始使用 Omnifaces(之前在 Tomcat 上安装过 CDI)和他们的 ViewScopedBean(在每次 POST/GET 请求后销毁,不像标准 JSF 2.3 中的 ViewScopedBean)。所以现在我们有可能将 Omnifaces/ViewScopedBean 与 SPA 方法一起使用。我希望,这是一个不错的选择。
猜你喜欢
  • 2013-09-02
  • 2012-09-26
  • 1970-01-01
  • 2014-02-14
  • 2012-06-09
  • 2015-04-15
  • 1970-01-01
  • 2012-09-29
  • 1970-01-01
相关资源
最近更新 更多