为啥 JSF 将 UI 组件的状态保存在服务器上?

Posted

技术标签:

【中文标题】为啥 JSF 将 UI 组件的状态保存在服务器上?【英文标题】:Why JSF saves the state of UI components on server?为什么 JSF 将 UI 组件的状态保存在服务器上? 【发布时间】:2011-07-25 08:43:39 【问题描述】:

    JSF 会在什么时间点将 UI 组件的状态保存在服务器端以及 UI 组件的状态信息何时从服务器内存中删除?当应用程序上的登录用户浏览页面时,组件的状态是否会继续在服务器上累积?

    我不明白 在服务器上保持 UI 组件状态有什么好处!? 直接将经过验证/转换的数据传递给托管 bean 还不够吗?我可以或应该尽量避免吗?

    如果有数千个并发用户会话,这不会消耗服务器端的太多内存吗?我有一个应用程序,用户可以在其中发布有关某些主题的博客。这个博客的规模相当大。当有回帖或浏览博客的请求时,会不会把这个大页面数据保存为组件状态的一部分?这样会占用太多内存。这不是一个问题吗?


更新 1:

现在,在使用 JSF 时不再需要保存状态。可以使用高性能的无状态 JSF 实现。有关详细信息和讨论,请参阅此 blog 和 this question。此外,还有一个开放的issue 包含在 JSF 规范中,这是一个为 JSF 提供无状态模式的选项。 (P.S. 如果这对您有用,请考虑为问题投票 this 和 this。)

更新 2 (24-02-2013):

Mojarra 2.1.19 推出无状态模式的好消息!

看这里:

http://weblogs.java.net/blog/mriem/archive/2013/02/08/jsf-going-stateless?force=255

http://java.net/jira/browse/JAVASERVERFACES-2731

http://balusc.blogspot.de/2013/02/stateless-jsf.html

【问题讨论】:

【参考方案1】:

为什么JSF需要在服务器端保存UI组件的状态?

因为 HTTP 是无状态的,而 JSF 是有状态的。 JSF 组件树会受到动态(编程)更改的影响。 JSF 只需要知道表单显示给最终用户时的确切状态,这样它就可以在表单提交回时根据原始 JSF 组件树提供的信息成功处理整个 JSF 生命周期服务器。组件树提供有关请求参数名称、必要的转换器/验证器、绑定的托管 bean 属性和操作方法的信息。


JSF 直到什么时间点才将 UI 组件的状态保存在服务器端,以及 UI 组件的状态信息究竟是什么时候从服务器内存中删除的?

这两个问题似乎可以归结为同一个问题。无论如何,这是特定于实现的,并且还取决于状态是保存在服务器上还是客户端上。当它过期或队列已满时,一个有点体面的实现将删除它。例如,当状态保存设置为会话时,Mojarra 的默认限制为 15 个逻辑视图。这可以使用web.xml 中的以下上下文参数进行配置:

<context-param>
    <param-name>com.sun.faces.numberOfLogicalViews</param-name>
    <param-value>15</param-value>
</context-param>

另请参阅 Mojarra FAQ 了解其他 Mojarra 特定参数和此相关答案 com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews


当应用程序上的登录用户浏览页面时,组件的状态是否会继续在服务器上累积?

从技术上讲,这取决于实施。如果您谈论的是逐页导航(只是 GET 请求),那么 Mojarra 不会在会话中保存任何内容。但是,如果它们是 POST 请求(带有命令链接/按钮的表单),那么 Mojarra 将保存会话中每个表单的状态,直到达到最大限制。这使最终用户能够在同一会话中的不同浏览器选项卡中打开多个表单。

或者,当状态保存设置为客户端时,JSF 不会在会话中存储任何内容。您可以通过web.xml 中的以下上下文参数来做到这一点:

<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
</context-param>

然后它将被序列化为隐藏输入字段中的加密字符串,表单名称为javax.faces.ViewState


我不明白将 UI 组件的状态保留在服务器端有什么好处。直接将经过验证/转换的数据传递给托管 bean 还不够吗?我可以/应该尽量避免吗?

这还不足以确保 JSF 的完整性和健壮性。 JSF 是一个具有单一控制入口点的动态框架。如果没有状态管理,人们将能够以某种方式欺骗/破解 HTTP 请求(例如,操纵 disabledreadonlyrendered 属性),让 JSF 做不同的 - 并且可能是危险的 - 事情。它甚至容易受到 CSRF 攻击和网络钓鱼。


如果有数千个并发用户会话,这不会在服务器端消耗太多内存吗?我有一个应用程序,用户可以在其中发布有关某些主题的博客。这个博客的规模相当大。当有回帖或查看博客的请求时,大型博客将被保存为组件状态的一部分。这会消耗太多内存。这不是一个问题吗?

内存特别便宜。只需给应用服务器足够的内存。或者,如果网络带宽对您来说更便宜,只需将状态保存切换到客户端。要找到最佳匹配,只需使用预期的最大并发用户数对您的 web 应用进行压力测试和分析,然后为应用服务器提供 125% ~ 150% 的最大测量内存。

请注意,JSF 2.0 在状态管理方面有了很大改进。可以保存部分状态(例如将保存&lt;h:form&gt; 而不是从&lt;html&gt; 一直到最后的全部内容)。例如,Mojarra 就是这样做的。具有 10 个输入字段(每个字段带有标签和消息)和 2 个按钮的平均表单占用的空间不超过 1KB。会话中有 15 个视图,每个会话不应超过 15KB。对于大约 1000 个并发用户会话,应该不超过 15MB。

您应该更关注会话或应用程序范围内的真实对象(托管 bean 和/甚至数据库实体)。我见过很多代码和项目,它们不必要地将整个数据库表复制到 Java 的内存中,以使用 Java 而不是 SQL 来过滤/分组/排列记录的会话范围 bean。对于大约 1000 条记录,每个用户会话很容易超过 10MB。

【讨论】:

你能澄清#2吗?为什么不能在每个请求上重新创建组件树?在请求之间实际需要存储什么状态,为什么?似乎在请求之间保存组件树的唯一原因是为了提高性能。 这里有更集中的问题:***.com/questions/7349174/… 你的回答对我来说很清楚! JSF 性能和可伸缩性的问题与程序员的实现有着内在的关系!需要知道以正确的方式使用技术! "只要给应用服务器足够的内存。"呃,如果我们谈论的是数千个并发用户会话,我觉得我们在这里谈论的是企业级的东西。如果您在企业中有企业级设备,则不能像在家中的 linux 机器中那样“给 appserver 足够的内存”。 您的回答非常清楚,谢谢。如果将 javax.faces.PARTIAL_STATE_SAVING 设置为 true,为什么 JSF 闪存范围会消失。 getFacesContext().getExternalContext().getFlash().put("buildingId", buildingId)。

以上是关于为啥 JSF 将 UI 组件的状态保存在服务器上?的主要内容,如果未能解决你的问题,请参考以下文章

JSF 2.0 部分状态保存似乎不起作用

JSF 2.0:跨多个视图保留组件状态

使用 JSF ui:repeat 循环执行带状态的逻辑

使用 Relay 的嵌套表单组件 UI 中的状态应该在哪里?

JSF ui:composition 和复合组件的问题

Java EE开发技术课程第六周(jsffacelets)