JSF视图在每个ajax请求上进行重建

我的JSF / RichFaces / Facelets ajax请求存在性能问题,而且我可以告诉它,因为整个组件树正在每个ajax请求上重建。 即使我使用ajaxSingle = true,在a4j:区域中包装部分,声明单个部分进行重新渲染或者根本不进行渲染,也会发生这种情况。 我们的页面是一个包含许多嵌套级别的动态页面。 该页面可能包含大约800-900个字段(inputText,富日历,selectOneMenus等)。 初始加载时间是一个问题,但我理解这个问题,它有很多领域。 一旦我们有了初始构建/渲染时间,虽然我们已经将所有其他操作设计为ajax并且只需要reRender。 从facelets调试日志我在任何ajax调用上看到这样的消息:

2011-08-24 22:19:03,054 DEBUG [facelets.viewhandler] (http-0.0.0.0-8080-2) Took 24445ms to build view: /oconsole/appfile.xhtml 2011-08-24 22:19:09,377 DEBUG [facelets.viewhandler] (http-0.0.0.0-8080-2) Took 6323ms to render view: /oconsole/appfile.xhtml 

我不确定我们正在做的事情是否导致整个组件树的重建,或者facelets是否正在确定由于某种原因(过时缓存?)所需的这种需求。 这是我们的堆栈:JBoss 5.1 JSF 1.2 RichFaces。 3.3.3.Final Facelets 1.1.15 Seam 2.1.2

我已经尝试添加一些上下文参数来查看它们是否有用,但它们什么都没做:facelets.BUILD_BEFORE_RESTORE = false facelets.REFRESH_PERIOD = -1或5(如5分钟)

无论如何要告诉我们的观点是否正确缓存? 我们没有delcare一个状态保存方法,所以我认为它默认为服务器端。 我们所有的请求都发生在长时间运行的对话中。 我不确定这是否会影响因为我认为视图会话缓存在会话级别? 非常感谢任何帮助,谢谢。

更多调试后更新:

AjaxViewHandler(具有FaceletsViewHandler的成员变量)具有developmentMode = true set。 我不确定这是否会导致facelets不缓存任何视图,因此任何更改都会在开发周期中刷新… ?? 很难找到关于facelets / JSF缓存视图以及行为和控制的任何信息。 此外,当我添加配置参数时:

  facelets.DEVELOPMENT false  

这没用! 在调试器中,我仍然看到真正的设置。 由于我们有很多子视图,我还尝试了com.sun.faces.numberOfLogicalViews和com.sun.faces.numberOfViewsInSession从15(默认值)到1000,这没有任何效果。

我也试过改变客户端状态保存没有任何运气。 用完了想法……希望有人可以帮忙….

似乎Seam 2.1自动初始化了RichFaces,我不确定它是否与它有关…..

与任何性能问题一样,分析器将有助于找到瓶颈。 (是的,您知道它是restore_view阶段,但不是restore_view阶段中的位置)。

也就是说,恢复视图阶段确实恢复了整个视图,而不仅仅是要处理或渲染的部分。 引用RichFaces taglib文档 :

process:Id [‘s](以调用UIComponent.findComponent()的格式)组件,在由此组件引起的AjaxRequest的情况下在阶段2-5处理。 可以是单个id,以逗号分隔的Id列表,也可以是带有数组或集合的EL表达式

RESTORE_VIEW是第1阶段。另外:

reRender:Id [的](以调用UIComponent.findComponent()的格式)组件,在由此组件引起的AjaxRequest的情况下呈现。 可以是单个id,以逗号分隔的Id列表,也可以是带有数组或集合的EL表达式

此外,我不确定UIComponent.findComponent()是使用比组件树更合适的数据结构实现的。 (在组件树中查找某些内容将归结为线性搜索…)。

我观察到与JSF 2.0(Mojarra)类似的效果。 我的结论是,无论是否渲染,视图都不得包含超过十几个UIComponents。 (换句话说,AJAX不适合页面导航。)我们通过仅包含视图中当前可见的组件来缩小视图,并在需要显示许多新组件时切换视图。 也就是说,我们有10个视图,每个视图只包含一个标签的内容,而不是一个包含10个标签的视图,每个视图包含30个组件。 该方法的缺点是在切换标签时布置组件,导致未保留在支持bean中的任何状态丢失。

我并不认为这是一个很好的解决方案。 唉,这是我几周前看到的最好的一个。 我也很乐意被展示出更好的一个。

编辑当我说恢复时,我的意思是ViewHandler.restoreView() ,它同时调用初始获取请求和回发。 说restoreView只是按原样重用现有视图是不正确的。 例如,JSF 2.0规范要求见7.6.2.7节:]

restoreView()方法必须履行以下职责:

所有实现都必须:

  • 如果无法识别viewId,则返回null
  • 调用关联的StateManagerrestoreView()方法,传递当前请求和计算的viewId的FacesContext实例,并返回返回的UIViewRoot ,它可以为null

在第7.7.2节中:

根据javax.faces.STATE_SAVING_METHOD初始化参数的值,JSF实现支持两种保存状态的主要机制(请参见第11.1.3节“应用程序配置参数”)。 此参数的可能值给出了要使用的方法的一般指示,同时允许JSF实现在技术细节上进行创新:

  • 客户 – […]
  • server – 使保存的状态在请求之间存储在服务器上。 希望使其已保存状态故障转移到不同容器实例的实现在实现其服务器端状态保存策略时必须牢记这一点。 默认实现在客户端和服务器模式下序列化视图。 在服务器模式下,此序列化视图存储在会话中,并且用于检索视图的唯一键将向下发送到客户端。 通过在会话中存储序列化视图,可以使用容器提供的常用机制进行故障转移。

换句话说,添加到JSF的AJAX支持(RichFaces 3添加到JSF 1.2中的一个,以及JSF 2.0中包含的一个)旨在减少网络带宽消耗,而不是服务器端cpu消耗。

根据我的分析,问题是由facelets的实现引起的。 基于调试器, FaceletViewHandler类的以下几行导致在每个(甚至是AJAX)请求上重建树( buildBeforeRestore为false,因此调用了buildView方法):

  // build view - but not if we're in "buildBeforeRestore" // land and we've already got a populated view. Note // that this optimizations breaks if there's a "c:if" in // the page that toggles as a result of request processing - // should that be handled? Or // is this optimization simply so minor that it should just // be trimmed altogether? if (!this.buildBeforeRestore || viewToRender.getChildren().isEmpty()) { this.buildView(context, viewToRender); } 

因此在我的脑海中解决每个请求上树重建的问题是深入到facelets实现并重新实现…我宁愿选择重构视图并最小化组件数量,因此构建树时间很短。