“绑定”属性如何在JSF中起作用?应该何时以及如何使用?

时间:2013-02-16 14:09:18

标签: jsf jsf-2 binding components

有很多材料可以区分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运行以返回name bean的User属性值。该值将打印到HTML输出。

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

下面是手动美化和评论后生成的输出的相关部分(请注意,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在哪里存储?

2 个答案:

答案 0 :(得分:120)

它是如何工作的?

当构建/恢复JSF视图(Facelets / JSP文件)时,将生成JSF组件树。此时,view build time,所有binding属性都会被评估(along with id attribtues and taghandlers like JSTL)。当需要在添加到组件树之前创建JSF组件时,JSF将检查binding属性是否返回预先创建的组件(即非null),如果是,则使用它。如果它没有预先创建,那么JSF将自动创建组件&#34;通常的方式&#34;并使用自动处理的组件实例作为参数调用binding属性后面的setter。

在效果中,它将组件树中的组件实例的引用绑定到范围变量。在生成的组件本身的HTML表示中,此信息无法显示。无论如何,此信息与生成的HTML输出无关。提交表单并恢复视图后,JSF组件树将从头开始重建,所有binding属性将仅重新评估,如上段所述。重新创建组件树后,JSF会将JSF视图状态还原到组件树中。

组件实例是请求范围的!

了解和理解的重要一点是具体的组件实例是有效的请求范围。它们是在每个请求上新创建的,它们的属性在恢复视图阶段填充了JSF视图状态的值。因此,如果将组件绑定到辅助bean的属性,那么支持bean应绝对不在比请求范围更广的范围内。另见JSF 2.0 specitication第3.1.5章:

  

3.1.5组件绑定

     

...

     

组件绑定通常与通过Managed动态实例化的JavaBeans结合使用   Bean Creation工具(参见第5.8.1节“VariableResolver和默认VariableResolver”)。 强烈   建议应用程序开发人员放置由组件绑定表达式指向的托管bean   “request”scope。这是因为将它放在会话或应用程序范围内需要线程安全,因为   UIComponent实例依赖于在单个线程内部运行。对...也有潜在的负面影响   将组件绑定放在“会话”范围内时的内存管理。

否则,组件实例在多个请求之间共享,可能导致&#34; duplicate component ID&#34;错误和&#34;怪异&#34;行为,因为视图中声明的验证器,转换器和侦听器将从先前的请求重新附加到现有组件实例。症状很明显:它们被执行多次,每次请求的次数都与组件绑定的范围相同。

并且,在负载很重的情况下(即,当多个不同的HTTP请求(线程)同时访问和操作同一个组件实例时),您可能迟早会遇到应用程序崩溃的情况。 Stuck thread at UIComponent.popComponentFromEL,或Java Threads at 100% CPU utilization using richfaces UIDataAdaptorBase and its internal HashMap,甚至某些&#34;奇怪的&#34;当JSF忙于保存或恢复视图状态(即堆栈跟踪指示IndexOutOfBoundsExceptionConcurrentModificationException方法等)时,saveState()restoreState()直接来自JSF实现源代码。

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

无论如何,以这种方式使用binding,将整个组件实例绑定到bean属性,甚至是在请求范围的bean上,在JSF 2.x中是一个相当罕见的用例,通常不是最佳实践。它表明了一种设计气味。您通常在视图方面声明组件,并将其运行时属性(如value)以及styleClassdisabledrendered等其他属性绑定到普通bean属性。然后,您只需操作所需的bean属性,而不是抓取整个组件并调用与该属性关联的setter方法。

如果某个组件需要动态构建&#34;基于静态模型,最好是在view build time tags like JSTL中使用tag file,而不是createComponent()new SomeComponent()getChildren().add(),而不是<ui:repeat>。另请参阅How to refactor snippet of old JSP to some JSF equivalent?

或者,如果组件需要动态呈现&#34;基于动态模型,只需使用iterator component<h:dataTable><cc:implementation>等)。另请参阅How to dynamically add JSF components

复合材料组件是一个完全不同的故事。将<cc:interface componentType>内的组件绑定到支持组件(即由binding标识的组件是完全合法的。另请参阅ao Split java.util.Date over two h:inputText fields representing hour and minute with f:convertDateTimeHow to implement a dynamic list with a JSF 2.0 Composite Component?

仅在本地范围内使用binding

但是,有时您希望了解特定组件内部不同组件的状态,而不仅仅是与操作/值相关验证相关的用例。为此,可以使用binding属性,但与bean属性结合使用。您可以在binding="#{foo}"属性中指定本地EL范围唯一变量名称,如UIComponent,并且组件在同一视图中的其他位置的呈现响应期间直接与#{foo}引用可用{{1}}。以下是几个相关问题,其中答案中使用了这样的解决方案:

另见:

答案 1 :(得分:1)

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

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

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

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