JSF view getting rebuild on each ajax request

寵の児 提交于 2019-12-01 03:07:51

As with any performance problem, a profiler would help greatly in locating the bottlenecks. (Yes, you know it is the restore_view phase, but not where in the restore_view phase).

That said, the restore view phase indeed restores the entire view, not just the parts that will be processed or rendered. Quoting the RichFaces taglib documentation:

process: Id['s] (in format of call UIComponent.findComponent()) of components, processed at the phases 2-5 in case of AjaxRequest caused by this component. Can be single id, comma-separated list of Id's, or EL Expression with array or Collection

RESTORE_VIEW is phase 1. Also:

reRender: Id['s] (in format of call UIComponent.findComponent()) of components, rendered in case of AjaxRequest caused by this component. Can be single id, comma-separated list of Id's, or EL Expression with array or Collection

Moreover, I am not sure that UIComponent.findComponent() is implemented using a more suitable datastructure than a component tree. (Finding something in the component tree would boil down to linear search ...).

I have observed similar effects with JSF 2.0 (Mojarra). My conclusion was that views must not contain more than a couple dozen UIComponents, irrespective of whether they are rendered. (Put differently, AJAX is unsuitable for page navigation.) We indend to keep views small by including only components that are currently visible in the view, and switch views if many new components need to appear. That is, instead of one view with 10 tabs with 30 components each, we'd have 10 views, each only containing the content of a single tab. A drawback of that approach is that components are disposed when switching tabs, causing any state not held in backing beans to be lost.

I do not claim this to be a good solution. Alas, it was the best one I found when looking into this a couple weeks ago. I'd be happy to be shown a better one, too.

Edit When I say restore, I mean ViewHandler.restoreView(), which called both for an initial get request and a postback. It is incorrect to say that restoreView would simply reuse an existing view as is. For instance, the JSF 2.0 spec mandates in section 7.6.2.7: ]

The restoreView() method must fulfill the following responsibilities:

All implementations must:

  • If no viewId could be identified, return null.
  • Call the restoreView() method of the associated StateManager , passing the FacesContext instance for the current request and the calculated viewId, and return the returned UIViewRoot, which may be null.

and in section 7.7.2:

JSF implementations support two primary mechanisms for saving state, based on the value of the javax.faces.STATE_SAVING_METHOD initialization parameter (see Section 11.1.3 “Application Configuration Parameters”). The possible values for this parameter give a general indication of the approach to be used, while allowing JSF implementations to innovate on the technical details:

  • client -- [...]
  • server -- Cause the saved state to be stored on the server in between requests. Implementations that wish to enable their saved state to fail over to a different container instance must keep this in mind when implementing their server side state saving strategy. The default implementation Serializes the view in both the client and server modes. In the server mode, this serialized view is stored in the session and a unique key to retrieve the view is sent down to the client. By storing the serialized view in the session, failover may happen using the usual mechanisms provided by the container.

Put differently, the AJAX support added to JSF (both the one added by RichFaces 3 to JSF 1.2, and the one incorporated into JSF 2.0) aims to reduce network bandwith consumption, not server side cpu consumption.

From my analysis the issue is caused by the implementation of facelets. Based on debugger the following lines of FaceletViewHandler class, causes rebuild of tree on each (even AJAX) request (buildBeforeRestore is false, so buildView method is invoked):

        // 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);
        }

So in my mind to resolve the issue of tree rebuild on each request is to go deeply to facelets implementation and do reimplementation... I would rather prefer restructure the view and minimize number of components, so build tree time is low.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!