JSF 中的“绑定”属性如何工作?何时以及如何使用它?
Posted
技术标签:
【中文标题】JSF 中的“绑定”属性如何工作?何时以及如何使用它?【英文标题】:How does the 'binding' attribute work in JSF? When and how should it be used? 【发布时间】:2013-04-16 04:32:42 【问题描述】:有很多材料可以区分 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 运行以返回 User
bean 的 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
存储在哪里?
【问题讨论】:
【参考方案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 版本是如何发生这种情况的。
【讨论】:
我会说我只是在服务器端动态生成组件时使用了组件绑定,设置了action
和value
等所有属性,然后让JSF框架完成他的工作.
我将user
存储为应用程序范围的托管bean,当我每次单击链接时,只有value="-908991273579182886:-7278326187282654551"
中的第二个数字发生变化,其他一切都相同。想知道这些有什么魔力。【参考方案2】:
它是如何工作的?
当构建/恢复 JSF 视图(Facelets/JSP 文件)时,将生成 JSF 组件树。在那一刻,view build time,所有binding
属性都被评估(along with id
attribtues and taghandlers like JSTL)。当需要在添加到组件树之前创建 JSF 组件时,JSF 会检查binding
属性是否返回一个预先创建的组件(即非null
),如果是,则使用它。如果它不是预先创建的,那么 JSF 将“以通常的方式”自动创建组件,并以自动创建的组件实例作为参数调用 binding
属性后面的设置器。
实际上,它将组件树中组件实例的引用绑定到一个作用域变量。此信息在生成的组件本身的 HTML 表示中是不可见的。无论如何,此信息与生成的 HTML 输出无关。当提交表单并恢复视图时,JSF 组件树只是从头开始重建,所有binding
属性将重新评估,如上段所述。组件树重新创建后,JSF会将JSF视图状态恢复到组件树中。
组件实例是请求范围的!
了解和理解的重要一点是,具体的组件实例是有效的请求范围。它们是在每个请求上新创建的,并且它们的属性在恢复视图阶段由来自 JSF 视图状态的值填充。因此,如果您将组件绑定到支持 bean 的属性,那么支持 bean绝对不能位于比请求范围更广的范围内。另请参阅JSF 2.0 specitication 第 3.1.5 章:
3.1.5 组件绑定
...
组件绑定通常与通过 Managed 动态实例化的 JavaBean 结合使用 Bean 创建工具(请参见第 5.8.1 节“VariableResolver 和默认的 VariableResolver”)。 强烈 建议应用程序开发人员将组件绑定表达式指向的托管 bean 放在 “请求”范围。 这是因为将它放在会话或应用程序范围内需要线程安全,因为 UIComponent 实例依赖于在单个线程内运行。也有潜在的负面影响 将组件绑定放在“会话”范围内时的内存管理。
否则,组件实例在多个请求之间共享,可能导致“duplicate component ID”错误和“奇怪”行为,因为视图中声明的验证器、转换器和侦听器从先前的请求重新附加到现有组件实例( s)。症状很明显:它们被执行多次,每个请求在与组件绑定到的相同范围内执行一次。
而且,在重负载下(即当多个不同的 HTTP 请求(线程)同时访问和操作同一个组件实例时),您可能迟早会面临应用程序崩溃,例如Stuck thread at UIComponent.popComponentFromEL,或Threads stuck at 100% CPU utilization in HashMap during JSF saveState(),甚至是一些“奇怪”的IndexOutOfBoundsException
或ConcurrentModificationException
,当JSF 忙于保存或恢复视图状态时(即堆栈跟踪指示saveState()
或@ 987654354@ 方法等)。
此外,由于单个组件基本上通过getParent()
和getChildren()
引用整个组件树的其余部分,当将单个组件绑定到视图或会话范围的 bean 时,您实际上是在保存整个 JSF 组件树在 HTTP 会话中一无所获。当视图中的组件相对较多时,这将在可用服务器内存方面变得非常昂贵。
在 bean 属性上使用 binding
是不好的做法
无论如何,以这种方式使用binding
,将整个组件实例绑定到一个bean 属性,即使是在请求范围的bean 上,在JSF 2.x 中也是一个相当少见的用例,通常不是最佳实践。它表示设计气味。您通常在视图端声明组件并将它们的运行时属性(如value
,可能还有其他如styleClass
、disabled
、rendered
等)绑定到普通bean 属性。然后,您只需准确地操作您想要的 bean 属性,而不是抓取整个组件并调用与该属性关联的 setter 方法。
如果需要基于静态模型“动态构建”组件,最好使用view build time tags like JSTL,如果需要,可以使用tag file,而不是createComponent()
、new SomeComponent()
、getChildren().add()
什么不是。另见How to refactor snippet of old JSP to some JSF equivalent?
或者,如果需要基于动态模型“动态渲染”组件,则只需使用iterator component(<ui:repeat>
、<h:dataTable>
等)。另见How to dynamically add JSF components。
复合组件是一个完全不同的故事。将<cc:implementation>
中的组件绑定到支持组件(即由<cc:interface componentType>
标识的组件)是完全合法的。另见a.o. Split java.util.Date over two h:inputText fields representing hour and minute with f:convertDateTime 和How to implement a dynamic list with a JSF 2.0 Composite Component?
仅在本地范围内使用binding
但是,有时您想从特定组件内部了解不同组件的状态,这在与操作/值相关验证相关的用例中更常见。为此,可以使用 binding
属性,但 不能 与 bean 属性结合使用。您可以在 binding
属性中指定本地 EL 范围内的唯一变量名称,就像 binding="#foo"
一样,并且组件在与 UIComponent
引用可用的 #foo
相同的视图中的其他地方直接呈现响应期间。以下是在答案中使用了这种解决方案的几个相关问题:
Validate input as required only if certain command button is pressed
How to render a component only if another component is not rendered?
JSF 2 dataTable row index without dataModel
Primefaces dependent selectOneMenu and required="true"
Validate a group of fields as required when at least one of them is filled
How to change css class for the inputfield and label when validation fails?
Getting JSF-defined component with Javascript
Use an EL expression to pass a component ID to a composite component in JSF
(那只是上个月的……)
另见:
How to use component binding in JSF right ? (request-scoped component in session scoped bean) View scope: java.io.NotSerializableException: javax.faces.component.html.HtmlInputText Binding attribute causes duplicate component ID found in the view【讨论】:
我将用户设置为请求范围的 bean。并在 getlink 和 setlink 方法中尝试了 sysout。登陆页面时=User.User(), User.getLink(), User.setLink(), User.getValue()
点击链接时=User.User(), User.setLink()...
为什么 setLink() 被再次调用,而另一个 UICommand 对象已经创建了?
“如果你绑定 [...],那么支持 bean 绝对不应该在比请求范围更广的范围内”。这在 JSF 2.2 中仍然成立吗?如果我必须使用 getChildren().addAll(...) 以编程方式在页面中包含组件,那么最好的选择是什么?
@Rinaldo:使用 JSTL 以编程方式仅以 XML 语法构建视图。
@Shirgill:确实。 UIViewRoot
(基本上所有UIComponent
)无论如何都是不可序列化的。以上是关于JSF 中的“绑定”属性如何工作?何时以及如何使用它?的主要内容,如果未能解决你的问题,请参考以下文章
如何(以及何时?)在 JSF 2.0 中删除 Session 范围的 bean
在 JSF 中如何以及何时销毁 @ViewScoped bean?