JSF生命周期和定制组件
关于在JSF中开发自定义组件,我很难理解一些事情。出于这些问题的目的,我们可以假定所有自定义控件都使用值绑定/表达式(而不是文字绑定),但是我也对它们的解释感兴趣。
- 我在哪里设置值绑定的值?这应该发生在解码中吗?还是应该解码做其他事情,然后在encodeBegin中设置值?
- 从值绑定中读取-何时从值绑定中读取数据与从已提交值中读取数据并将其放入值绑定中?
- 何时在与所有这些有关的形式上调用动作侦听器? JSF生命周期页面都提到在各个步骤发生的事件,但是当我仅调用命令按钮的简单侦听器时,这对我来说并不完全清楚
我已经尝试了几种组合,但是总是以难以发现的错误结束,我认为这些错误是由于对事件生命周期的基本误解造成的。
解决方案
回答
在"调用应用程序"阶段(即最终的"渲染响应"阶段之前的最后一个阶段),将调用诸如CommandButton之类的操作侦听器。这在JSF Lifecycle图1中显示。
回答
JSF规范中有一个很好的图表,该图表显示了了解这些内容所必需的请求生命周期。
这些步骤是:
- 恢复视图。 UIComponent树将重建。
- 应用请求值。可编辑组件应实现EditableValueHolder。此阶段遍历组件树并调用processDecodes方法。如果该组件不是像UIData这样复杂的组件,则除了调用其自己的解码方法之外,它不会做任何其他事情。除了找到其渲染器并调用其解码方法(将自身作为参数传递)以外,decode方法并没有做太多事情。渲染器的工作是获取任何提交的值并通过setSubmittedValue对其进行设置。
- 流程验证。此阶段调用processValidators,后者将调用validate。 validate方法采用提交的值,使用任何转换器对其进行转换,使用任何验证器对其进行验证,然后(假设数据通过了这些测试)调用setValue。这会将值存储为局部变量。当此局部变量不为null时,将返回该变量,而不返回任何对getValue的调用的值绑定中的值。
- 更新模型值。此阶段称为processUpdates。在输入组件中,这将调用updateModel,它将获取ValueExpression并调用它来设置模型上的值。
- 调用应用程序。按钮事件侦听器等将在这里被调用(如果有内存,导航也会被调用)。
- 渲染响应。通过渲染器渲染树并保存状态。
- 如果这些阶段中的任何一个失败(例如,值无效),则生命周期将跳至"渲染响应"。
- 在大多数这些阶段之后,可以触发各种事件,并适当地调用侦听器(例如在"流程验证"之后的值更改侦听器)。
这是事件的某种简化版本。有关更多详细信息,请参考规范。
我会问为什么我们要编写自己的UIComponent。这是一项艰巨的任务,需要对JSF架构有深入的了解才能使其正确。如果需要自定义控件,最好创建一个具体的控件,以使用等效的渲染器扩展现有的UIComponent(如HtmlInputText一样)。
如果污染不是问题,那么可以使用Apache MyFaces形式的开源JSF实现。
回答
It is the only framework that I've ever used where component creation is a deep intricate process like this. None of the other web frameworks (whether in the .net world or not) make this so painful, which is completely inexplicable to me.
当我们考虑目标时,JSF背后的一些设计决策开始变得更有道理。 JSF被设计为可工具化,它公开了许多用于IDE的元数据。 JSF不是Web框架,它是可以用作Web框架的MVP框架。 JSF具有高度的可扩展性和可配置性,我们可以在每个应用程序的基础上替换90%的实现。
如果我们只想添加一个额外的HTML控件,那么大多数这些事情只会使工作变得更加复杂。
The component is a composition of several inputtext (and other) base components, btw.
我假设基于JSP包含/基于工具的页面片段不符合要求。
我会考虑使用UIComponentELTag.createComponent来创建一个具有UIPanel基础的复合控件,并从现有实现中创建其所有子级。 (我假设我们正在使用JSP / taglibs并做出其他一些猜测。)如果现有的UIPanel渲染器都没有完成此工作,则可能需要自定义渲染器,但是渲染器很容易。
回答
我发现最好的文章是Jsf Component Writing,
至于2我在哪里读取组件中的值绑定的值,我们有一个看起来像这样的吸气剂
public String getBar() { if (null != this.bar) { return this.bar ; } ValueBinding _vb = getValueBinding("bar"); return (_vb != null) ? (bar) _vb.getValue(getFacesContext()) : null; } how did this get into the getValueBinding? In your tag class setProperties method 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."); } }