You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于JSF规范中UIComponent.invokeOnComponent()方法行为的技术问询

JSF UIComponent.invokeOnComponent() 预期行为探讨:遍历子组件时是否需调用pushComponentToEL?

你遇到的这个问题其实戳中了JSF规范里一个容易被忽略的细节——UIComponent.invokeOnComponent()在遍历子组件时的上下文维护逻辑。咱们来一步步拆解分析:

问题概述

我在嵌套使用两个带有独立后台组件的复合组件时遇到了异常行为:invokeOnComponent()方法在遍历子组件过程中并未调用pushComponentToEL(),但ImplicitELResolver依赖UIComponent.getCurrentCompositeComponent()来解析#{cc.xx}这类属性值。这就引发了疑问:invokeOnComponent()在遍历子组件前,是否应该像调用回调前那样执行pushComponentToEL()?我已经在后台Bean重写invokeOnComponent()方法时手动实现了这个逻辑作为临时解决方案,但不确定这么做会不会带来其他未知问题。

详细场景

主页面代码

<html>
<compositeComponent:comp1>
<compositeComponent:comp2>
</html>

复合组件comp1代码

<composite:interface componentType="comp1">
</composite:interface>
<composite:implementation>
<myComponentLib:myComponent />
</composite:implementation>

对应后台组件BACKING_COMPONENT_1。

复合组件comp2代码

<composite:interface componentType="comp2">
</composite:interface>
<composite:implementation>
<p:dataTable value="#{cc.myList}" >
</p:dataTable>
</composite:implementation>

对应后台组件BACKING_COMPONENT_2。

异常触发流程

  • 调用复合组件1的encodeAll方法
  • 执行复合组件1的encodeBegin,调用pushComponentToEL(),更新CURRENT_COMPOSITE_COMPONENT_STACK
  • 调用myComponent的encodeAll,其渲染器需要查找组件,进而调用UIViewRoot的invokeOnComponent()
  • 递归调用到复合组件2内DataTable的invokeOnComponent()方法
  • DataTable需要解析#{cc.myList},ImplicitELResolver通过UIComponent.getCurrentCompositeComponent()获取基类,但此时依赖的CURRENT_COMPOSITE_COMPONENT_STACK返回的是BACKING_COMPONENT_1而非预期的BACKING_COMPONENT_2

问题分析

从JSF规范的定义来看,这里确实存在一个模糊地带:规范仅明确了invokeOnComponent()在找到目标clientId并调用回调前需要执行pushComponentToEL()和popComponentToEL(),但并未规定在遍历facetsAndChildren子组件的过程中是否需要同样执行这对操作。

而你的场景恰恰暴露了这个漏洞:当嵌套复合组件时,遍历子组件的过程中如果不维护CURRENT_COMPOSITE_COMPONENT_STACK,后续子组件的EL解析就会错误地继承父复合组件的上下文,导致#{cc}指向了错误的后台组件。

疑问解答

1. 遍历facetsAndChildren时调用invokeOnComponent是否应使用push/pop机制?

答案是肯定的。从JSF的EL上下文设计逻辑来看,CURRENT_COMPOSITE_COMPONENT_STACK是用来跟踪当前EL解析上下文所属的复合组件的,任何进入子复合组件的操作都应该更新这个栈,离开时恢复。invokeOnComponent()在遍历子组件时,本质上是进入了子组件的上下文,所以必须调用pushComponentToEL()来更新栈,否则后续的#{cc}解析必然出错。

2. 这个workaround是否会引发其他未知问题?

只要你的实现严格遵循push/pop的成对调用原则,一般不会有问题:

  • 确保在调用子组件的invokeOnComponent()前调用pushComponentToEL()
  • 务必用try/finally块保证在子组件调用完成后(无论成功还是失败)调用popComponentToEL(),避免栈溢出或上下文混乱
  • 不要破坏原有invokeOnComponent()的核心逻辑(比如找到目标组件后的回调执行逻辑)

另外,建议你检查下使用的JSF实现(比如Mojarra、MyFaces)的最新版本,部分厂商可能已经针对这个规范模糊点补全了实现逻辑,如果升级版本能解决问题,就不需要自己维护这个workaround了。

内容的提问来源于stack exchange,提问作者xqvier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:49:29