在每个 ajax 请求上重建 JSF 视图
Posted
技术标签:
【中文标题】在每个 ajax 请求上重建 JSF 视图【英文标题】:JSF view getting rebuild on each ajax request 【发布时间】:2011-11-03 15:35:55 【问题描述】:我的 JSF/RichFaces/Facelets ajax 请求存在性能问题,据我所知,因为每个 ajax 请求都在重建整个组件树。即使我使用 ajaxSingle=true,将部分包装在 a4j:region 中,声明一个用于重新渲染的部分或根本不声明,也会发生这种情况。我们的页面是具有许多嵌套级别的动态页面。该页面可能包含大约 800-900 个字段(inputText、丰富的日历、selectOneMenus 等)。初始加载时间是一个问题,但我理解这个问题,它涉及很多领域。一旦我们有了初始构建/渲染时间,尽管我们已经将所有其他操作设计为 ajax 并且只重新渲染需要的内容。从 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 富脸。 3.3.3.决赛 Facelets 1.1.15 接缝 2.1.2
我尝试添加一些上下文参数以查看它们是否有帮助,但它们什么也没做: facelets.BUILD_BEFORE_RESTORE = false facelets.REFRESH_PERIOD = -1 或 5(如 5 分钟)
有没有办法判断我们的视图是否被正确缓存?我们不关心状态保存方法,所以我相信它默认为服务器端。我们所有的请求都发生在接缝的长期对话中。我不确定这是否是一个因素,因为我认为视图会在会话级别缓存?任何帮助将不胜感激,谢谢。
更多调试后更新:
AjaxViewHandler(它有一个 FaceletsViewHandler 的成员变量)设置了 developmentMode=true。我不确定这是否会导致 facelets 不缓存任何视图,因此在开发周期中会刷新任何更改...??很难找到关于视图的 facelets/JSF 缓存以及行为和控制的任何信息。此外,当我添加配置参数时:
<context-param>
<param-name>facelets.DEVELOPMENT</param-name>
<param-value>false</param-value>
</context-param>
这没用!在调试器中,我仍然看到真正的设置。由于我们有很多子视图,我也尝试过 com.sun.faces.numberOfLogicalViews 和 com.sun.faces.numberOfViewsInSession 从 15(默认)增加到 1000,这没有效果。
我还尝试更改为客户端状态保存,但没有任何运气。没有想法了....希望有人可以提供帮助....
Seam 2.1 似乎自动初始化 RichFaces,我不确定这是否与它有关.....
【问题讨论】:
我知道这是一个老问题......但是有这么多领域,你可以考虑这样做:industrieit.com/blog/2011/11/… 【参考方案1】:根据我的分析,问题是由 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 实现并重新实现......我宁愿重组视图并最大限度地减少组件数量,因此构建树的时间很短。
【讨论】:
【参考方案2】:与任何性能问题一样,分析器将极大地帮助定位瓶颈。 (是的,你知道这是 restore_view 阶段,但不是 restore_view 阶段)。
也就是说,恢复视图阶段确实恢复了整个视图,而不仅仅是将要处理或渲染的部分。引用RichFaces taglib documentation:
process:组件的Id['s](调用UIComponent.findComponent()的格式),在2-5阶段处理,以防由该组件引起的AjaxRequest。可以是单个 id、逗号分隔的 Id 列表或带有数组或集合的 EL 表达式
RESTORE_VIEW 是第 1 阶段。另外:
reRender:组件的Id['s](调用UIComponent.findComponent()的格式),在该组件引起的AjaxRequest的情况下渲染。可以是单个 id、逗号分隔的 Id 列表或带有数组或集合的 EL 表达式
此外,我不确定 UIComponent.findComponent() 是使用比组件树更合适的数据结构实现的。 (在组件树中查找内容归结为线性搜索...)。
我在 JSF 2.0 (Mojarra) 中观察到了类似的效果。我的结论是视图不能包含超过几十个 UIComponent,无论它们是否被渲染。 (换句话说,AJAX 不适合页面导航。)我们打算通过仅包含当前在视图中可见的组件来保持视图较小,并在需要出现许多新组件时切换视图。也就是说,我们将有 10 个视图,每个视图只包含一个选项卡的内容,而不是一个视图有 10 个选项卡,每个选项卡有 30 个组件。这种方法的一个缺点是,在切换选项卡时会释放组件,导致任何未保存在支持 bean 中的状态丢失。
我不认为这是一个好的解决方案。唉,这是我几周前研究这个时发现的最好的一个。我也很高兴看到一个更好的人。
编辑
当我说恢复时,我的意思是ViewHandler.restoreView()
,它同时调用了初始获取请求和回发。说restoreView
会简单地重用现有视图是不正确的。例如,JSF 2.0 规范在第 7.6.2.7 节中规定:]
restoreView()
方法必须履行以下职责:所有实现必须:
如果无法识别 viewId,则返回null
。 调用关联StateManager
的restoreView()
方法,传递FacesContext
实例为 当前请求和计算出的viewId,并返回返回的UIViewRoot
,可能是null
。
在第 7.7.2 节中:
JSF 实现支持两种保存状态的主要机制,基于 javax.faces.STATE_SAVING_METHOD 初始化参数(参见第 11.1.3 节“应用程序配置 参数”)。该参数的可能值给出了要使用的方法的一般指示,同时允许 在技术细节上创新的 JSF 实现:
客户 -- [...] server -- 导致在请求之间将保存的状态存储在服务器上。希望启用的实现 他们保存的状态以故障转移到不同的容器实例时,在实现他们的服务器时必须牢记这一点 侧面国家储蓄策略。默认实现在客户端和服务器模式下序列化视图。在里面 服务器模式,这个序列化的视图存储在会话中,检索视图的唯一键被发送到 客户。通过将序列化视图存储在会话中,可以使用由 容器。
换句话说,添加到 JSF 的 AJAX 支持(RichFaces 3 添加到 JSF 1.2 的支持,以及合并到 JSF 2.0 的支持)旨在减少网络带宽消耗,而不是服务器端 CPU 消耗。
【讨论】:
Meriton 感谢您的意见,但我很困惑。 Ajax 的全部意义在于能够在幕后进行非常快速的部分重新渲染。我认为 JSF/Facelets/Richfaces 存储/缓存视图,如果可以的话,将重用构建的组件树。当您在上面说“恢复”时,我认为我们需要明确从缓存中恢复或重建。我主要担心我已经通过 RichFaces 标记指定了所有信息,我只需要在此操作上更新页面的一个很小的子集,为什么它会构建整个树? 我已经扩展了我的答案来解决你的问题。 再次感谢 Meriton 的跟进。看来我们唯一的选择是调整构建这些动态视图并通过设计将其分解的逻辑的性能。从 JSF/Facelets 设计的角度来看,这非常令人失望,在不必要的时候效率如此之低,我不明白。以上是关于在每个 ajax 请求上重建 JSF 视图的主要内容,如果未能解决你的问题,请参考以下文章