jsf 2 - 'binding' 属性在 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如何维护与Userbean的link属性的绑定?

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

它是如何工作的

当构建/恢复JSF视图(Facelets/JSP文件)时,将生成一个JSF组件树。此时,即视图构建时,将评估所有binding属性(以及id属性和像JSTL这样的标记处理程序)。当JSF组件在添加到组件树之前需要创建时,JSF将检查binding属性是否返回预创建的组件(即非null),如果返回,则使用它;通常的方式";并以自动创建的组件实例为自变量调用CCD_ 15属性后面的setter。

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

组件实例是请求范围的

重要的是要知道和理解具体的组件实例是有效的请求范围。它们在每个请求中都是新创建的,并且它们的属性在恢复视图阶段填充了JSF视图状态中的值。因此,如果您将组件绑定到backingbean的属性,那么backingbean绝对不应该在比请求范围更宽的范围内。另请参阅JSF 2.0规范第3.1.5章:

3.1.5组件绑定

组件绑定通常与JavaBeans结合使用,JavaBeans通过ManagedBean创建工具(参见第5.8.1节"VariableResolver和默认VariableResolve")非常强烈建议应用程序开发人员将组件绑定表达式指向的托管bean放置在"请求"范围这是因为将它放在会话或应用程序范围中需要线程安全,因为UIComponent实例依赖于在单个线程内部运行。对在"会话"范围中放置组件绑定时的内存管理。

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

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

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

当然,这一切可能可以通过在所有地方添加synchronized来确保JSF组件的线程安全来解决,但它们永远不会在不同的浏览器选项卡/窗口/会话之间共享,因为这对最终用户来说只会以"结束;wtf"行为,而且这会极大地降低表现。

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

将整个组件实例绑定到bean属性,即使是在请求范围的bean上,在正确设计的JSF应用程序中也是一种非常罕见的用例,通常也不是最佳实践。它表示有设计气味。通常,您在视图端声明组件,并将它们的运行时属性(如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.Split java.util.Date over two h:inputText字段,用f:convertDateTime表示小时和分钟,以及如何用JSF 2.0复合组件实现动态列表?

仅在本地作用域中使用binding

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

  • 只有在按下特定命令按钮时才根据需要验证输入

  • 如何仅在未渲染另一个组件的情况下渲染该组件?

  • JSF 2数据表没有数据的行索引模型

  • Primefaces依赖于selectOneMenu和required=";真";

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

  • 验证失败时,如何更改inputfield和标签的css类?

  • 使用Javascript 获取JSF定义的组件

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

    (这只是上个月的…)

但我需要尽快修复现有的火车残骸

如果您有一个现有的JSF应用程序,其中binding在大于请求的范围内滥用引用bean,并且您只想在尽可能短的时间内修复它,以修复严重的内存和线程安全问题,那么您最好的选择是(regex)find&更换以下形式的getter和setter:

public SomeComponent getSomeComponent() {
return someComponent;
}
public void setSomeComponent(SomeComponent someComponent) {
this.someComponent = someComponent;
}

转换为以下形式:

public SomeComponent getSomeComponent() {
return getBoundComponent("someComponent");
}
public void setSomeComponent(SomeComponent someComponent) {
setBoundComponent("someComponent", someComponent);
}

使用以下辅助方法,这些方法基本上将它们保存在请求范围中:

protected static <C extends UIComponent> C getBoundComponent(String key) {
return (C) getBoundComponents().get(key);
}
protected static <C extends UIComponent> void setBoundComponent(String key, C component) {
getBoundComponents().put(key, component);
}
private static <C extends UIComponent> Map<String, C> getBoundComponents() {
return (Map<String, C>) FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
.computeIfAbsent("com.example.BOUND_COMPONENTS", $ -> new HashMap<>());
}

然后让IDE(auto-)删除未使用的字段。

另请参阅:

  • 如何正确使用JSF中的组件绑定?(会话作用域bean中的请求作用域组件)
  • 查看范围:java.io.NotSerializableException:javax.faces.component.html.HtmlInputText
  • Binding属性导致在视图中发现重复的组件ID

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

  • 确保每个from输入都有一个完全唯一的名称,这样当表单提交回呈现它的组件树时,就可以很容易地判断每个组件可以在哪里读取其值表单
  • JSF组件可以生成提交回serer的javascript,生成的javascript也知道每个组件绑定在哪里,因为它是由组件生成的
  • 对于像链接这样的内容,您可以将绑定信息作为查询参数、url本身的一部分或matrx参数包含在url中。例如。

    CCD_ 43将允许jsf在组件树中查看链接123被点击。也可以是htp:..../jsf;LinkId=123

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

相关内容

  • 没有找到相关文章

最新更新