JSF生命周期和自定义组件

19

关于在JSF中开发自定义组件,我有几个不太理解的问题。 对于这些问题,您可以假设所有自定义控件都使用值绑定/表达式(而不是文字绑定),但我也对它们的解释感兴趣。

  1. 在哪里设置值绑定的值? 这应该在decode中完成吗?还是decode应该执行其他操作,然后在encodeBegin中设置值?
  2. 从值绑定读取 - 何时从值绑定中读取数据,而何时从提交的值中读取数据并将其放入值绑定中?
  3. 在所有这些事件中,表单上的操作侦听器是何时被调用的? JSF生命周期页面都提到了各种步骤中发生的事件,但仅对于一个简单的命令按钮的侦听器被调用的情况不是很清楚。

我尝试过一些组合,但总是遇到难以找到的错误,我认为这是基本误解引起的事件生命周期。


收到这个问题正在接受编辑活动的通知,感觉有点有趣,毕竟已经过去了将近10年。当时的Stack Overflow还是一个简单得多的地方! - jsight
4个回答

20

JSF规范中有一张非常好的图表展示了请求生命周期,这对于理解这些内容很重要。

具体步骤如下:

  • 恢复视图。重建UI组件树。
  • 应用请求值。可编辑的组件应该实现EditableValueHolder接口。此阶段遍历组件树并调用processDecodes方法。如果组件不像UIData之类的复杂对象,那么它就只会调用自身的decode方法。decode方法只是查找它的渲染器并调用其decode方法,将自己作为参数传递。渲染器的任务是获取任何提交的值,并通过setSubmittedValue设置它。
  • 处理验证。此阶段调用processValidators方法,该方法将调用validate方法。validate方法接受提交的值,使用任何转换器进行转换,使用任何验证器进行验证,(假设数据通过了这些测试)则调用setValue方法。这将将该值存储为本地变量。只要该本地变量不为null,它将被返回,而不是在任何调用getValue的情况下从值绑定中返回的值。
  • 更新模型值。此阶段调用processUpdates方法。对于输入组件,这将调用updateModel方法,该方法将获取ValueExpression并调用它来设置模型上的值。
  • 调用应用程序。按钮事件监听器等将在此处被调用(如果我没记错的话,导航也是如此)。
  • 渲染响应。树通过渲染器进行渲染,并保存状态。
  • 如果其中任何一个阶段失败(例如,值无效),则生命周期将跳过到“渲染响应”阶段。
  • 在大多数这些阶段之后,可能会触发各种事件,相应地调用侦听器(例如,在处理验证后触发值更改侦听器)。

这只是事件的一个简化版本。请参考规范以获取更多详细信息。

我会怀疑你为什么要编写自己的UIComponent。 这是一项非常棘手的任务,需要深入了解JSF架构才能做到正确。如果您需要自定义控件,则最好创建一个具体控件,该控件扩展现有的UIComponent(例如HtmlInputText),并具有等效的渲染器。

如果污染不是问题,则可以使用Apache MyFaces的开源JSF实现。


4

动作监听器(例如CommandButton的)在调用应用程序阶段被调用,这是最后一个阶段,在最终的呈现响应阶段之前。如JSF生命周期-图1所示。


3
这是我使用过的唯一一个组件创建如此深奥复杂的框架。其他任何网络框架(无论是在 .net 世界还是其他领域)都不会让这变得如此痛苦,这对我来说完全无法解释。
当你考虑到目标时,JSF 的一些设计决策开始变得更加合理。JSF 被设计为工具 - 它为 IDE 公开了大量元数据。JSF 不是网络框架 - 它是一个 MVP 框架,可用作网络框架。JSF 高度可扩展和可配置 - 您可以在每个应用程序基础上替换 90% 的实现。
如果您只想插入额外的 HTML 控件,则大多数内容只会使您的工作更加复杂。
顺便说一下,该组件是几个 inputtext(和其他)基本组件的组合。
我假设 JSP-包含/基于工具的页面片段不符合您的要求。
我会考虑使用您的UIComponentELTag.createComponent来创建一个基于UIPanel的复合控件,并从现有实现中创建所有子控件。(我假设您正在使用JSPs/taglibs并进行一些其他猜测。) 如果没有现有的UIPanel渲染器能够完成工作,您可能需要一个自定义渲染器,但渲染器很容易实现。

1

我找到的最好的文章是Jsf组件编写, 至于第二个问题,在您的组件中读取值绑定的值在哪里?您有一个看起来像这样的getter:


public String getBar() {  
     if (null != this.bar) {  
         return this.bar ;  
     }  
     ValueBinding _vb = getValueBinding("bar");  
     return (_vb != null) ? (bar) _vb.getValue(getFacesContext()) : null;  
}
</code>  
</pre>

<p>这是如何进入getValueBinding的呢?
在您的标签类setProperties方法中。</p>

<pre><code>  if (bar!= null) {  
         if (isValueReference(bar)) {  
             ValueBinding vb = Util.getValueBinding(bar);  
             foo.setValueBinding("bar", vb);  
         } else {  
             throw new IllegalStateException("The value for 'bar' must be a ValueBinding.");  
         }  
     }  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接