JSF 中的“绑定"属性如何工作?何时以及如何使用它? [英] How does the 'binding' attribute work in JSF? When and how should it be used?

查看:26
本文介绍了JSF 中的“绑定"属性如何工作?何时以及如何使用它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有很多材料可以区分 JSF 中的 value 属性和 binding 属性.

There are lot of materials out there differentiating value attribute and binding attribute in JSF.

我对这两种方法有何不同很感兴趣.给定:

I'm interested in how both approaches differ from each other. Given:

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 输出.

It is pretty straight forward what happens when a value attribute is specified. The getter runs to return the name property value of the User bean. The value is printed to HTML output.

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

But I couldn't understand how binding works. How does the generated HTML maintain a binding with the link property of the User bean?

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

Below is the relevant part of the generated output after manual beautification and commenting (note that the id j_id_jsp_1847466274_1 was auto-generated and that there are two hidden input widgets). I'm using Sun's JSF RI, version 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存放在哪里?

推荐答案

它是如何工作的?

当构建/恢复 JSF 视图(Facelets/JSP 文件)时,将生成 JSF 组件树.那一刻,查看构建时间,所有binding评估属性(连同 id 属性和标记处理程序,如 JSTL).当需要创建 JSF 组件才能添加到组件树中时,JSF 会检查 binding 属性是否返回预先创建的组件(即非null),如果是,然后使用它.如果它不是预先创建的,那么 JSF 将以通常的方式"自动创建组件.并以自动创建的组件实例作为参数调用 binding 属性后面的设置器.

How does it work?

When a JSF view (Facelets/JSP file) get built/restored, a JSF component tree will be produced. At that moment, the view build time, all binding attributes are evaluated (along with id attribtues and taghandlers like JSTL). When the JSF component needs to be created before being added to the component tree, JSF will check if the binding attribute returns a precreated component (i.e. non-null) and if so, then use it. If it's not precreated, then JSF will autocreate the component "the usual way" and invoke the setter behind binding attribute with the autocreated component instance as argument.

实际上,它将组件树中组件实例的引用绑定到一个作用域变量.此信息在生成的组件本身的 HTML 表示中是不可见的.无论如何,此信息与生成的 HTML 输出无关.当提交表单并恢复视图时,JSF 组件树只是从头开始重建,所有 binding 属性都将重新评估,如上段所述.组件树重新创建后,JSF会将JSF视图状态恢复到组件树中.

In effects, it binds a reference of the component instance in the component tree to a scoped variable. This information is in no way visible in the generated HTML representation of the component itself. This information is in no means relevant to the generated HTML output anyway. When the form is submitted and the view is restored, the JSF component tree is just rebuilt from scratch and all binding attributes will just be re-evaluated like described in above paragraph. After the component tree is recreated, JSF will restore the JSF view state into the component tree.

了解和理解的重要一点是,具体的组件实例是有效的请求范围.它们是在每个请求上新创建的,并且它们的属性在恢复视图阶段由来自 JSF 视图状态的值填充.因此,如果您将组件绑定到支持 bean 的属性,那么支持 bean绝对不能位于比请求范围更广的范围内.另请参阅 JSF 2.0 规范 第 3.1 章.5:

Important to know and understand is that the concrete component instances are effectively request scoped. They're newly created on every request and their properties are filled with values from JSF view state during restore view phase. So, if you bind the component to a property of a backing bean, then the backing bean should absolutely not be in a broader scope than the request scope. See also JSF 2.0 specitication chapter 3.1.5:

...

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

Component bindings are often used in conjunction with JavaBeans that are dynamically instantiated via the Managed Bean Creation facility (see Section 5.8.1 "VariableResolver and the Default VariableResolver"). It is strongly recommend that application developers place managed beans that are pointed at by component binding expressions in "request" scope. This is because placing it in session or application scope would require thread-safety, since UIComponent instances depends on running inside of a single thread. There are also potentially negative impacts on memory management when placing a component binding in "session" scope.

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

Otherwise, component instances are shared among multiple requests, possibly resulting in "duplicate component ID" errors and "weird" behaviors because validators, converters and listeners declared in the view are re-attached to the existing component instance from previous request(s). The symptoms are clear: they are executed multiple times, one time more with each request within the same scope as the component is been bound to.

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

And, under heavy load (i.e. when multiple different HTTP requests (threads) access and manipulate the very same component instance at the same time), you may face sooner or later an application crash with e.g. Stuck thread at UIComponent.popComponentFromEL, or Threads stuck at 100% CPU utilization in HashMap during JSF saveState(), or even some "strange" IndexOutOfBoundsException or ConcurrentModificationException coming straight from JSF implementation source code while JSF is busy saving or restoring the view state (i.e. the stack trace indicates saveState() or restoreState() methods and like).

此外,当将单个组件绑定到视图或会话范围的 bean,您实际上将整个 JSF 组件树保存在 HTTP 会话中.当视图中的组件相对较多时,这将在可用服务器内存方面变得非常昂贵.

Also, as a single component basically references the rest of the entire component tree via getParent() and getChildren(), when binding a single component to a view or session scoped bean, you're essentially saving the entire JSF component tree in the HTTP session for nothing. This will get really costly in terms of available server memory when you have relatively a lot of components in the view.

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

Regardless, using binding this way, binding a whole component instance to a bean property, even on a request scoped bean, is in JSF 2.x a rather rare use case and generally not the best practice. It indicates a design smell. You normally declare components in the view side and bind their runtime attributes like value, and perhaps others like styleClass, disabled, rendered, etc, to normal bean properties. Then, you just manipulate exactly that bean property you want instead of grabbing the whole component and calling the setter method associated with the attribute.

在需要动态构建"组件的情况下,基于静态模型,更好的是使用 查看构建时间标签,如 JSTL,如有必要,请在 标记文件,而不是 createComponent()new SomeComponent()getChildren().add() 等等不是.另请参阅 如何将旧 JSP 片段重构为一些 JSF 等价物?

In cases when a component needs to be "dynamically built" based on a static model, better is to use view build time tags like JSTL, if necessary in a tag file, instead of createComponent(), new SomeComponent(), getChildren().add() and what not. See also How to refactor snippet of old JSP to some JSF equivalent?

或者,如果一个组件需要动态渲染"基于动态模型,然后只需使用 迭代器组件 (<ui:repeat><h:dataTable> 等).另请参阅如何动态添加 JSF 组件.

Or, if a component needs to be "dynamically rendered" based on a dynamic model, then just use an iterator component (<ui:repeat>, <h:dataTable>, etc). See also How to dynamically add JSF components.

复合组件是一个完全不同的故事.将 内的组件绑定到支持组件(即由 标识的组件是完全合法的.另见 ao 拆分 java.util.Date用 f:convertDateTime如何用JSF 2.0 Composite Component实现动态列表?

Composite components is a completely different story. It's completely legit to bind components inside a <cc:implementation> to the backing component (i.e. the component identified by <cc:interface componentType>. See also a.o. Split java.util.Date over two h:inputText fields representing hour and minute with f:convertDateTime and How to implement a dynamic list with a JSF 2.0 Composite Component?

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

However, sometimes you'd like to know about the state of a different component from inside a particular component, more than often in use cases related to action/value dependent validation. For that, the binding attribute can be used, but not in combination with a bean property. You can just specify an in the local EL scope unique variable name in the binding attribute like so binding="#{foo}" and the component is during render response elsewhere in the same view directly as UIComponent reference available by #{foo}. Here are several related questions where such a solution is been used in the answer:

如何仅在另一个组件未渲染时才渲染一个组件?

没有dataModel的JSF 2 dataTable行索引

Primefaces 依赖 selectOneMenu 和 required=true"

当至少有一个字段被填写时,根据需要验证一组字段

验证失败时如何更改输入字段和标签的css类?

使用 Javascript 获取 JSF 定义的组件

使用 EL 表达式将组件 ID 传递给 JSF 中的复合组件

(那只是上个月的……)

这篇关于JSF 中的“绑定"属性如何工作?何时以及如何使用它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆