它是如何工作的?
当构建/恢复 JSF 视图(Facelets/JSP 文件)时,将生成 JSF 组件树。在那一刻,视图构建时间,所有binding
属性都被评估(以及属性id
和标签处理程序,如 JSTL)。当需要创建 JSF 组件才能添加到组件树中时,JSF 会检查该binding
属性是否返回一个预先创建的组件(即 non- null
),如果是,则使用它。如果它不是预先创建的,那么 JSF 将“以通常的方式”自动创建组件,并使用自动创建的组件实例作为参数调用 setterbinding
属性。
实际上,它将组件树中组件实例的引用绑定到范围变量。此信息在生成的组件本身的 HTML 表示中是不可见的。无论如何,此信息与生成的 HTML 输出无关。当提交表单并恢复视图时,JSF 组件树只是从头开始重建,所有binding
属性都将重新评估,如上段所述。重新创建组件树后,JSF 会将 JSF 视图状态恢复到组件树中。
组件实例是请求范围的!
重要的是要知道和理解具体的组件实例是有效的请求范围。它们是在每个请求上新创建的,并且它们的属性在恢复视图阶段由来自 JSF 视图状态的值填充。因此,如果您将组件绑定到支持 bean 的属性,那么支持 bean绝对不应该在比请求范围更广泛的范围内。另请参阅JSF 2.0 规范第 3.1.5 章:
3.1.5 组件绑定
...
组件绑定通常与通过托管 Bean 创建工具动态实例化的 JavaBean 一起使用(请参阅第 5.8.1 节“VariableResolver 和默认变量解析器”)。强烈建议应用程序开发人员将组件绑定表达式指向的托管 bean 放在“请求”范围内。这是因为将它放在会话或应用程序范围内需要线程安全,因为 UIComponent 实例依赖于在单个线程内运行。将组件绑定置于“会话”范围内时,还可能对内存管理产生负面影响。
否则,组件实例在多个请求之间共享,可能导致“重复组件 ID ”错误和“奇怪”行为,因为视图中声明的验证器、转换器和侦听器从先前的请求重新附加到现有组件实例。症状很明显:它们被执行多次,每个请求在与组件绑定到的相同范围内执行一次。
而且,在重负载下(即,当多个不同的 HTTP 请求(线程)同时访问和操作同一个组件实例时),您可能迟早会面临应用程序崩溃,例如在 UIComponent.popComponentFromEL 处卡住线程,或线程卡住在 JSF saveState() 期间,HashMap 中的 CPU 利用率为 100%,甚至在 JSF 忙于保存或恢复视图状态(即堆栈跟踪指示或方法等)时,一些“奇怪”IndexOutOfBoundsException
或ConcurrentModificationException
直接来自 JSF 实现源代码。saveState()
restoreState()
此外,由于单个组件基本上通过getParent()
and引用整个组件树的其余部分getChildren()
,当将单个组件绑定到视图或会话范围的 bean 时,您实际上将整个 JSF 组件树保存在 HTTP 会话中。当视图中有相对较多的组件时,这将在可用服务器内存方面变得非常昂贵。
在 bean 属性上使用binding
是不好的做法
无论如何,使用binding
这种方式,将整个组件实例绑定到 bean 属性,即使在请求范围的 bean 上,在 JSF 2.xa 中也是相当罕见的用例,通常不是最佳实践。它表示设计气味。您通常在视图端声明组件并将它们的运行时属性(例如value
、styleClass
、等disabled
)绑定rendered
到普通的 bean 属性。然后,您只需准确地操作您想要的 bean 属性,而不是抓取整个组件并调用与该属性关联的 setter 方法。
如果需要基于静态模型“动态构建”组件,最好使用JSTL 之类的视图构建时间标签,如果需要,可以在标签文件中使用,而不是createComponent()
,等等。另请参阅如何将旧 JSP 片段重构为一些等效的 JSF?new SomeComponent()
getChildren().add()
或者,如果需要基于动态模型“动态渲染”组件,则只需使用迭代器组件(<ui:repeat>
、<h:dataTable>
等)。另请参阅如何动态添加 JSF 组件。
复合组件是一个完全不同的故事。将a内的组件绑定到<cc:implementation>
支持组件(即由<cc:interface componentType>
.一个 JSF 2.0 复合组件?
仅binding
在本地范围内使用
但是,有时您想从特定组件内部了解不同组件的状态,这比与动作/值相关验证相关的用例更常见。为此,binding
可以使用属性,但不能与 bean 属性结合使用。binding
您可以像这样在属性中指定本地 EL 范围唯一变量名称,binding="#{foo}"
并且组件在同一视图中的其他位置呈现响应期间直接作为UIComponent
可用的引用#{foo}
。以下是在答案中使用此类解决方案的几个相关问题:
也可以看看: