“ bind”属性在 JSF 中是如何工作的? 何时以及如何使用它?

在 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 运行返回 Userbean 的 name属性值。该值被打印到 HTML 输出中。

但我不明白 binding是如何工作的。生成的 HTML 如何维护与 User bean 的 link属性的绑定?

下面是经过手动美化和注释后生成的输出的相关部分(注意 id j_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存放在哪里?

115073 次浏览

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

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

    http:..../somelink?componentId=123允许 jsf 在组件树中查看链接123是否被单击。也可能是 htp:..../jsf;LinkId=123

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

它是怎么工作的?

当构建/恢复 JSF 视图(Faclets/JSP 文件)时,将生成一个 JSF 组件树。此时,对 查看构建时间、所有 binding属性进行评估(以及 id属性和诸如 JSTL 之类的标记处理程序)。当需要在将 JSF 组件添加到组件树之前创建该组件时,JSF 将检查 binding属性是否返回预先创建的组件(即非 null) ,如果返回,则使用它。如果没有预先创建,那么 JSF 将“通常的方式”自动创建组件,并使用自动创建的组件实例作为参数调用 binding属性后面的 setter。

实际上,它将组件树中组件实例的引用绑定到作用域变量。这些信息在组件本身生成的 HTML 表示形式中是不可见的。无论如何,这些信息与生成的 HTML 输出毫无关系。当提交表单并恢复视图时,JSF 组件树只是从头开始重新构建,所有的 binding属性只是像上段描述的那样被重新计算。在重新创建组件树之后,JSF 将把 JSF 视图状态恢复到组件树中。

组件实例是请求范围的!

了解和理解具体组件实例是有效的请求范围,这一点非常重要。它们是在每个请求上新创建的,并且在恢复视图阶段,它们的属性被来自 JSF 视图状态的值填充。因此,如果您将组件绑定到后台 bean 的属性,那么后台 bean 应该处于比请求范围更广的范围内。另见 JSF 2.0规范第3.1.5章:

3.1.5组件绑定

...

组件绑定通常与通过 Managed 动态实例化的 JavaBean 一起使用 Bean 创建工具(参见5.8.1节“ VariableResolver 和默认 VariableResolver”) 建议应用程序开发人员将组件绑定表达式指向的托管 bean 放在 这是因为将其放在会话或应用程序范围中需要线程安全,因为 UIComponent 实例依赖于在单个线程内运行 在“会话”范围内放置组件绑定时的内存管理。

否则,组件实例在多个请求之间共享,可能导致“ 重复的组件 ID”错误和“怪异”行为,因为视图中声明的验证器、转换器和侦听器被重新附加到先前请求的现有组件实例。其症状很明显: 它们被执行多次,每个请求在与组件绑定到的相同范围内再执行一次。

而且,在重负载下(即当多个不同的 HTTP 请求(线程)同时访问和操作同一个组件实例时) ,你可能迟早会面临应用程序崩溃,例如 卡在 UIComponent 的线程在 JSF saveState ()期间,HashMap 中的线程的 CPU 利用率停留在100% ,甚至一些“奇怪的”IndexOutOfBoundsExceptionConcurrentModificationException直接来自 JSF 实现源代码,而 JSF 正忙于保存或恢复视图状态(即堆栈跟踪指示 saveState()restoreState()方法等)。

此外,由于单个组件基本上通过 getParent()getChildren()引用整个组件树的其余部分,因此当将单个组件绑定到视图或会话范围的 bean 时,实际上是在 HTTP 会话中免费保存整个 JSF 组件树。如果视图中有相对较多的组件,那么就可用服务器内存而言,这将非常昂贵。

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

无论如何,以这种方式使用 binding,将整个组件实例绑定到 bean 属性,即使是在请求范围内的 bean 上,在 JSF 2.x 中也是相当罕见的用例,而且通常不是最佳实践。这说明有设计的味道。您通常在视图端声明组件,并将它们的运行时属性(如 value)或其他属性(如 styleClassdisabledrendered等)绑定到普通 bean 属性。然后,您只需精确地操作您想要的 bean 属性,而不是抓取整个组件并调用与该属性相关联的 setter 方法。

在需要基于静态模型“动态构建”组件的情况下,如果需要,最好使用 查看构建时间标记,如 JSTL,而不是 createComponent()new SomeComponent()getChildren().add()等等。参见 如何将旧 JSP 的片段重构为一些 JSF 等价物?

或者,如果组件需要基于动态模型“动态呈现”,那么只需使用 迭代器组件迭代器组件(<ui:repeat><h:dataTable>等)。参见 如何动态添加 JSF 组件

复合组件则完全不同。将 <cc:implementation>中的组件绑定到支持组件(即由 <cc:interface componentType>标识的组件)是完全合法的。参见 A.O.将 java.util.Date 分割为两个 h: inputText 字段,用 f: ConvertDateTime 表示小时和分钟如何使用 JSF 2.0复合组件实现动态列表?

仅在本地范围内使用 binding

但是,有时候您希望从特定组件内部了解不同组件的状态,这种情况在与操作/值相关的验证相关的用例中更为常见。为此,可以使用 binding属性,但是可以将 没有与 bean 属性组合使用。您只需在 binding属性中指定一个在本地 EL 范围内的唯一变量名,比如 binding="#{foo}",并且组件在与 #{foo}提供的 UIComponent引用直接相同的视图中其他地方的呈现响应期间。以下是几个相关问题,答案中使用了这种解决办法:

参见: