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

查看:38
本文介绍了'binding' 属性在 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 属性后面的 setter.

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 动态实例化的 JavaBeans 结合使用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?

或者,如果一个组件需要动态渲染"基于动态模型,然后只需使用 迭代器组件( 等).另请参阅如何动态添加 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 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:

如何仅在未呈现另一个组件的情况下呈现另一个组件?

无数据模型的 JSF 2 dataTable 行索引

Primefaces 依赖 selectOneMenu 和 required="true"

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

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

使用 Javascript 获取 JSF 定义的组件

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

(这只是上个月的...)

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

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