79

JSF中有很多区分value属性和属性的材料。binding

我对这两种方法有何不同感兴趣。鉴于:

public class User {
    private String name;
    private UICommand link;

    // Getters and setters omitted.
}
<h:form>
    <h:commandLink binding="#{user.link}" value="#{user.name}" />
</h:form>

value指定属性时发生的事情非常简单。getter 运行以返回bean的name属性值。User该值被打印到 HTML 输出。

但我无法理解它是如何binding工作的。生成的 HTML 如何与bean的link属性保持绑定?User

下面是手动美化和注释后生成的输出的相关部分(注意 idj_id_jsp_1847466274_1是自动生成的,并且有两个隐藏的输入小部件)。我正在使用 Sun 的 JSF RI,版本 1.2。

<form action="/TestJSF/main.jsf" enctype="application/x-www-form-urlencoded"
    id="j_id_jsp_1847466274_1" method="post"  name="j_id_jsp_1847466274_1">
    <input name="j_id_jsp_1847466274_1" type="hidden" value="j_id_jsp_1847466274_1">
    <a href="#" onclick="...">Name</a>
    <input autocomplete="off" id="javax.faces.ViewState" name="javax.faces.ViewState"
        type="hidden" value="-908991273579182886:-7278326187282654551">
</form>

这里的binding存储在哪里?

4

2 回答 2

135

它是如何工作的?

当构建/恢复 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 忙于保存或恢复视图状态(即堆栈跟踪指示或方法等)时,一些“奇怪”IndexOutOfBoundsExceptionConcurrentModificationException直接来自 JSF 实现源代码。saveState()restoreState()

此外,由于单个组件基本上通过getParent()and引用整个组件树的其余部分getChildren(),当将单个组件绑定到视图或会话范围的 bean 时,您实际上将整个 JSF 组件树保存在 HTTP 会话中。当视图中有相对较多的组件时,这将在可用服务器内存方面变得非常昂贵。

在 bean 属性上使用binding是不好的做法

无论如何,使用binding这种方式,将整个组件实例绑定到 bean 属性,即使在请求范围的 bean 上,在 JSF 2.xa 中也是相当罕见的用例,通常不是最佳实践。它表示设计气味。您通常在视图端声明组件并将它们的运行时属性(例如valuestyleClass、等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}。以下是在答案中使用此类解决方案的几个相关问题:

也可以看看:

于 2013-02-17T02:41:07.070 回答
1

每个 JSF 组件都将自己呈现为 HTML,并且可以完全控制它生成的 HTML。JSF 可以使用许多技巧,具体使用哪些技巧取决于您使用的 JSF 实现。

  • 确保每个 from 输入都有一个完全唯一的名称,这样当表单被提交回呈现它的组件树时,很容易判断每个组件可以从哪里读取其值表单。
  • JSF 组件可以生成 javascript 提交回服务器,生成的 javascript 也知道每个组件的绑定位置,因为它是由组件生成的。
  • 对于 hlink 之类的内容,您可以在 url 中包含绑定信息作为查询参数或作为 url 本身的一部分或作为 matrx 参数。举些例子。

    http:..../somelink?componentId=123将允许 jsf 查看组件树以查看链接 123 已被单击。或者它可以htp:..../jsf;LinkId=123

回答这个问题的最简单方法是创建一个只有一个链接的 JSF 页面,然后检查它产生的 html 输出。这样,您将确切地知道使用您正在使用的 JSF 版本是如何发生这种情况的。

于 2013-02-16T15:50:12.550 回答