为什么JSF保存组件树状态?

Rya*_*yan 24 jsf jsf-2 state-saving

托管bean状态和组件树状态之间似乎存在差异.您可以使用@RequestScoped@SessionScoped等注释来控制托管bean状态,但似乎您无法选择是否保存组件树状态(尽管您可以选择是否保存在服务器或客户端上) ).

似乎只有在单个请求的持续时间内才需要组件树状态作为临时数据结构来帮助处理请求.它应该从头开始为每个请求重建.使用JSF 2.0,部分状态保存使情况更好,因为只保存表单数据,但我不明白为什么甚至从前一个请求中形成数据是有用的.

如果您的应用程序仅使用请求范围托管bean,那么在请求之间保存组件树状态尤其没有意义.即使您的应用程序具有会话范围托管bean,我也会假设托管bean将保持状态,组件树仍然不需要在请求之间具有任何状态.

Arj*_*jms 21

添加到上一个答案,自从partial state saving默认使用JSF 2.0调用之后.

JSF(Facelets)中的默认视图描述语言在每个请求之后从原始Facelet创建整个组件树,并从其相应的标记属性初始化组件.然后它标志着国家.

然后将每个后续状态更改记为增量更改,并且实际上正在保存此状态.可能只是没有这样的变化,然后视图状态是空的(由于一个错误,状态从来没有真正空,但最近已经修复.请参阅http://java.net/ jira/browse/JAVASERVERFACES-2203了解详情)

所以最重要的问题是,当这个非空时,这个状态实际上是什么?

正如BalusC已经说过的那样,这可能会对组件树进行动态更改.这些更改可以从支持bean启动,也可以从静态组件启动.执行此动态更改的组件类型的一个简单示例是一个表组件,它根据数据集中的实际列数创建子列组件.

视图状态的另一个重要用法是记住组件内部已更改但尚未推入模型的值.这可以是轻触开关组件中的开关,移动拨号组件中的滑块等.

一个特定示例是viewParam组件,它记住初始化它的请求参数(GET或非面POST参数的查询字符串参数).有关更多信息,请参阅此内容:http://arjan-tijms.omnifaces.org/2011/07/stateless-vs-stateful-jsf-view.html

与状态组件有很强的关系,记住UI状态和失败的转换或验证.在这种情况下,UI组件将记住用户输入的值,并将记住存在转换/验证错误.

该州的另一种用法是优化.一些组件计算他们认为计算成本昂贵的值并将它们存储在视图状态中.例如,UIInput组件在第一次回发后执行此操作:

private boolean validateEmptyFields(FacesContext ctx) {

    if (validateEmptyFields == null) {
        ExternalContext extCtx = ctx.getExternalContext();
        String val = extCtx.getInitParameter(VALIDATE_EMPTY_FIELDS_PARAM_NAME);

        if (val == null) {
            val = (String) extCtx.getApplicationMap().get(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
        }
        if (val == null || "auto".equals(val)) {
            validateEmptyFields = isBeansValidationAvailable(ctx);
        } else {
            validateEmptyFields = Boolean.valueOf(val);
        }
    }

    return validateEmptyFields;

}
Run Code Online (Sandbox Code Playgroud)

在此之后validateEmptyFields存储在视图状态中,因此在下面的表单提交时不必再次计算.如果用户可以在重新计算或存储(众所周知的时空优化)之间进行选择,则会有所改进.

国家的概念是自早期概念以来困扰Web应用程序开发的内容.每个人都想拥有基本上有状态的互动,但几乎没有人愿意处理它甚至想想它.

JSF一直试图在这里提供答案,但它显然并不完美,而且还有改进的余地.JSF坚持能够恢复视图状态(甚至是空视图状态)可能很麻烦,尽管在另一个答案中提到它确实提供了针对CSRF的隐式保护.JSF 2.2将获得更明确的CSRF保护(参见http://arjan-tijms.omnifaces.org/p/jsf-22.html#869),所以我们将来可能会看到一些变化.

有一个选项可以关闭每个组件的状态并有一个简单的钩子来恢复状态,而框架不能(如在ASP.NET中)也可能有帮助.

  • 有时使用视图状态确实值得商榷,我即将为EG创建一些JIRA问题,要求解决这个问题.然而,托管bean并不总是正确的答案.很多这种状态涉及组件的内部细节以及处理这个问题的负担,如果不对用户强制执行恕我直言.我没有提及的另一个类别是优化(参见更新的答案) (3认同)
  • 感谢您添加更多说明和示例.我现在明白了一点.我仍然不相信保存组件树状态是正确的事情,因为它似乎总是可以使用托管bean工具来保持状态.我不明白为什么必须保存状态以重新显示转换/验证失败的用户输入的值 - 只是服务器端将帖子转发到原始视图. (2认同)

Bal*_*usC 18

因为可以根据初始请求以编程方式更改组件树.无论何时必须处理表单数据,这都不一定可以在后续请求中重现.

此外,我的印象是您认为组件树也包含模型值.这不是真的.它仅保存对模型值(托管bean属性)的引用(通过表达式语言).视图状态不复制/复制/包含模型状态.它只是一个纯UI组件树.也许你的困惑是基于此.请注意,术语"表单数据"将被解释为提交的值和模型值.

也可以看看:

  • 组件可以通过`UIViewRoot#findComponent()`访问,并可以通过例如`component.setAttribute("foo","bar")`进行编程修改,或者可以通过例如`component.getChildren()添加新的子代.add( newHiddenInput)`等等.这可能发生在托管bean中,或者更常见的是在"PhaseListener"中.确实,这不一定使用基本的JSF以简单的形式出现,但这可能发生在更复杂的表单或更高级的组件库中.如果重新创建新视图而不是使用上一个请求的呈现响应期间的状态,则所有这些更改都将丢失. (5认同)
  • 双提交预防令牌可以使用ViewScope保存在托管bean中,也可以直接保存在HTTPSession中,因此听起来组件树不一定要在请求之间保存.我查看了JSF规范,Ed Burns编写的JSF 2.0完整参考手册和Cay Horstmann编写的Core JSF第3版,并没有找到为什么必须保存组件树的单一解释.奇怪,是吧? (4认同)
  • @Ryan我甚至找不到小优势!:)你能告诉我它是什么??? (2认同)
  • @ M.ES - 对我来说也没什么意义.我承认你可以构建一个场景,其中组件树保存在将来的请求中使用的状态,但在我看来,在这些场景中将状态放在托管bean工具中会更好.我猜这个推理只是因为用户*可以*. (2认同)