在描述我遇到的问题之前,我先给出一些背景信息。
环境是JSF,Primefaces 3.5和GlassFish 3.1.2.2。
我正在处理用于编辑数据的应用程序。数据在几个表单之间分开,每个表单都在tabView的选项卡中。数据模型非常丰富,在某些情况下有几个嵌套的tabView。
为了简化开发,应用程序有一个描述表单字段的复合组件。它将字段的标签,输入,工具提示和其他图形组件分组。表单包含其中几个复合组件。
该应用程序使用GlassFish 3及其提供的Mojarra(版本2.1.6)进行设计和编码。首先,它广泛使用JSTL标签(c:if,c:forEach和c:choose),这些标签非常便于动态构建页面内容,具体取决于具体情况。
因此,复合组件有许多c:if标签来构建字段,具体取决于其类型(输入文本,组合框,日历......)。同样,应用程序最复杂的屏幕(使用3个嵌套的tabViews)是使用大量的c:if和c:选择标签构建的。
使用Mojarra 2.1.6,应用程序可以正常工作。但是在复杂的屏幕中,由于a bug in older Mojarra versions,应用程序的性能非常差。然后我们决定将Mojarra升级到最新的2.1版本,即2.1.27。
由于升级我们遇到问题,最重要的是在表单加载和AJAX更新期间出现重复的ID错误。重复ID始终涉及复合组件的内部组件。似乎复合组件连续两次使用相同的clientId进行实例化。显示示例的部分组件树如下:
+id: DataDashboard
type: org.primefaces.component.dashboard.Dashboard@58a26f8d
+id: Data_property
type: org.primefaces.component.panel.Panel@1049bf71
+id: j_idt1286
type: javax.faces.component.html.HtmlPanelGrid@59b79c11
+id: j_idt1287
type: javax.faces.component.UINamingContainer@7071dc24
+id: j_id4
type: javax.faces.component.UIPanel@25674e1b
+id: j_idt1288
type: javax.faces.component.html.HtmlPanelGrid@2138e1cc
+id: j_idt1289
type: com.sun.faces.facelets.tag.ui.ComponentRef@1d70291e
+id: j_idt1290
type: <html xmlns="http://www.w3.org/1999/xhtml">
+id: hType <===============
type: javax.faces.component.html.HtmlInputHidden@2e9b7fab
+id: lId
type: javax.faces.component.html.HtmlOutputLabel@54c26622
+id: j_idt1299
type: org.primefaces.component.tooltip.Tooltip@26bf6da7
+id: j_idt1300
type: org.primefaces.component.tooltip.Tooltip@61b802d0
(...)
+id: j_idt3300
type: javax.faces.component.html.HtmlPanelGrid@5a4da336
+id: j_idt3301
type: com.sun.faces.facelets.tag.ui.ComponentRef@1f165c8b
+id: j_idt3302
type: <html xmlns="http://www.w3.org/1999/xhtml">
+id: hType <===============
type: javax.faces.component.html.HtmlInputHidden@7515b99e
在阅读本网站上的文章后,例如this one或BalusC's blog,我决定摆脱可能涉及问题的JSTL标签(但我不能当然);无论如何这都会“更好”(或者是吗?)。
所以,而不是
<c:if test="#{someCriterion}">
(some stuff)
</c:if>
我将另一个名为someStuff.xhtml的文件外化(某些东西)并写入
<ui:include src="#{someCriterion ? 'someStuff.xhtml' : ''}" />
测试中使用的标准是复合组件的属性,Session Scoped Bean的属性,或上述标准的衍生物(函数或三元运算符)。
据我所知,我已经使用此方法转换了所有JSTL标记,并且应用程序的行为与转换前的行为相同。不幸的是,这包括重复的ID错误。
我还尝试使用测试用例应用程序复制错误,遗憾的是我还没有设法这样做。似乎简单的应用程序可以工作。
我很失落如何解决这个问题。任何帮助将不胜感激。
非常感谢。
编辑:添加了部分组件树。
答案 0 :(得分:1)
你遇到的问题很长。我很惊讶地看到它在Mojarra一次又一次地失败,我已经看到多年来同样问题的报道,而且随着时间的推移会更多。它之所以如此难以解决,是因为有必要对facelets原始算法进行一些重大改动,并将其与JSF 2.0 PSS算法混合使用,这样做真的很难。如果你使用c:if,c:choose或ui:include,它将无法按照你想要的方式使用Mojarra并启用PSS。
您可以尝试的一个选项是关闭Mojarra中的部分状态保存(PSS)(javax.faces.PARTIAL_STATE_SAVING web配置参数)。这样,PSS和facelets算法之间的问题消失了(因为你不再使用它,但性能不会是最好的)。有一个名为javax.faces.FULL_STATE_SAVING_VIEW_IDS的参数,可用于指示需要完全状态保存的视图,因此如果您有一个具有重复ID异常的视图,只需在参数中设置视图即可。
问题的最佳解决方案是切换到Apache MyFaces。 JSF for 2.0.x和2.1.x的实现解决了这个问题,两者都非常稳定。在JSF 2.2中,它完成了使用c:forEach标记的解决方案,但是最近修复了一次,所以如果你使用这些工件,请尝试使用2.2.1或更高版本。
我个人强烈推荐Apache MyFaces有很多原因。最新的代码非常稳定。如果您正在寻找最佳性能,请查看JSFCentral上的这篇文章Understanding JSF Performance。我希望它有所帮助。