1

我正在使用部署到 Tomcat 7 服务器的 JSF 2 Mojarra、RichFaces 4.1 和 EL 2.2。

在我的页面上,我有两个 a4j:regions 包含在同一个表单中。顶部区域是实体的 dataTable 列表。底部区域是详细信息编辑器面板。您从列表中选择一个实体,其详细信息会加载到编辑面板中。详细信息面板有自己的验证规则和提交按钮。我现在遇到了一个众所周知的问题,即当我将第二个实体加载到详细信息表单中时,JSF 没有重置提交的值并出现验证错误。

在研究解决这个问题的过程中,我发现了BalusC 的这个博客。他很好地分解了重置问题,并推荐了这个 JSF 2 action-listener来解决这个问题。

我将 ResetInputAjaxActionListener 合并到我的项目中,作为我 faces-config.xml 中定义的动作侦听器。但是,现在,代码在没有侦听器的情况下失败,它正在工作(只是没有重置提交的/本地/模型值)。

我的 JSF 页面包含一个 dataTable 并且每一行都有一个编辑 commandLink 来将该行的实体加载到编辑器中:

<a4j:commandLink action="#{editController.load(entity.id)}" render="@form" execute="@this" value="Edit"/>

由于我使用的是 EL 2.2,因此我将实体的主键直接传递给 load 方法。如果没有动作监听器,这将按预期工作。但是,当我启用动作侦听器时,代码会引发以下异常:

Caused by: javax.faces.FacesException: #{editController.load(entity.id)}: java.lang.NullPointerException
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:110)
at com.buksoft.automaint.web.util.ResetInputAjaxActionListener.processAction(ResetInputAjaxActionListener.java:163)
at javax.faces.component.UICommand.broadcast(UICommand.java:315)
at org.richfaces.component.RowKeyContextEventWrapper.broadcast(RowKeyContextEventWrapper.java:104)
at org.richfaces.component.UIDataAdaptor.broadcast(UIDataAdaptor.java:452)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
... 51 more
Caused by: javax.faces.el.MethodNotFoundException: java.lang.NullPointerException
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:104)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
... 58 more
Caused by: java.lang.NullPointerException
at java.lang.Class.isAssignableFrom(Native Method)
at org.apache.el.util.ReflectionUtil.isAssignableFrom(ReflectionUtil.java:299)
at org.apache.el.util.ReflectionUtil.getMethod(ReflectionUtil.java:172)
at org.apache.el.parser.AstValue.invoke(AstValue.java:251)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
... 59 more

在我看来,EL 参数值被设置为 null,当 EL 尝试将 null 绑定到操作方法的参数(预期为 Long 值)时,这会导致 NullPointerException。

现在是真正让我难过的部分。为了缩小错误范围,我注释掉了 ResetInputAjaxActionListener 的功能,使其基本上是只读的。我发现如果 findAndAddEditableValueHolders() 方法的这些行执行,那么在 ResetInputAjaxActionListener 完成后调用的 ActionListenerImpl.processAction() 会抛出异常:

if (target instanceof EditableValueHolder) {
    inputs.add((EditableValueHolder) target);
}
else if (context.getIdsToVisit() != VisitContext.ALL_IDS) {
    // Render ID didn't point an EditableValueHolder. Visit all children as well.
    findAndAddEditableValueHolders(VisitContext.createVisitContext(
    context.getFacesContext(), null, context.getHints()), target, inputs);
}

请参阅上面链接的 ResetInputAjaxActionListener 类中的 findAndAddEditableValueHolders() 方法。

而且问题代码实际上只在 else if 中,这让我很困惑,因为它是对方法本身的递归调用。几乎就像 createVisitContext() 方法正在修改后备视图树一样。正如我所说,如果我去掉所有进行任何真正更改的变异代码,并只执行这个树访问代码(只读),就会抛出异常。

谁能解释访问组件树(而不是修改其中的任何内容)如何破坏 EL 方法参数功能?

4

0 回答 0