@ViewScoped 在每个回发请求上调用 @PostConstruct

Posted

技术标签:

【中文标题】@ViewScoped 在每个回发请求上调用 @PostConstruct【英文标题】:@ViewScoped calls @PostConstruct on every postback request 【发布时间】:2011-07-29 08:20:54 【问题描述】:

这似乎不对。我正在清理我的代码,我才注意到这一点。每个 ajax 请求都在触发我的 @ViewScoped bean 的构造函数和 @PostConstruct。即使是简单的数据库分页也会触发它。

我 understood 认为 @ViewScoped@RequestScoped 长,并且不应该在每次请求时都重新构建它。仅在通过 GET 重新加载完整页面后。

【问题讨论】:

【参考方案1】:

换句话说,您的@ViewScoped bean 的行为类似于@RequestScoped bean。它在每个回发请求中都从头开始重新创建。这有很多可能的原因,其中大部分归结为相关的 JSF 视图在 JSF 状态下不再可用,而 JSF 状态又默认与 HTTP 会话相关联。

如果您可以确保 HTTP 会话本身不是问题的根本原因,即当 @SessionScoped 工作正常时,请查看以下可能原因列表。否则,如果 HTTP 会话本身也被丢弃并在每个请求上重新创建,那么您需要退后一步,查看会话 cookie 和服务器配置。任何与 HTTP 会话中断相关的原因至少都超出了 JSF 的上下文。

    您使用的是 Mojarra 2.1.17 或更早版本,并且视图包含 EL 表达式,这些表达式将视图范围的 bean 属性绑定到在view build time 期间评估的标记属性。例如 JSTL <c:if><c:forEach> 等或 JSF <ui:include><x:someComponent id="#..."<x:someComponent binding="#..."> 等。这是由 Mojarra (issue 1496) 中的错误引起的。另见Why does @PostConstruct callback fire every time even though bean is @ViewScoped? JSF

    这已在 Mojarra 版本 2.1.18 中修复。如果您无法升级到较新的版本,解决方法是在web.xml 中禁用部分状态保存,另见JSTL in JSF2 Facelets... makes sense?

     <context-param>
         <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
         <param-value>false</param-value>
     </context-param>
    

    或者当您只想针对一组特定的 JSF 视图时:

     <context-param>
         <param-name>javax.faces.FULL_STATE_SAVING_VIEW_IDS</param-name>
         <param-value>/foo.xhtml;/bar.xhtml;/folder/baz.xhtml</param-value>
     </context-param>
    

    重要的是,将 JSF 组件的 idbinding 属性的值绑定到视图范围的 bean 属性是一种不好的做法。这些确实应该绑定到请求范围的 bean 属性,或者应该寻求替代方案。另见How does the 'binding' attribute work in JSF? When and how should it be used?

    您使用的是 Mojarra 2.2.0,只有该版本在维护 2.2.1 中已修复的视图范围方面存在(但未知)错误,另请参见 issue 2916。解决方案是升级到新版本。

    @ViewScoped 注解是从错误的包中导入的。 JSF 提供了两个@ViewScoped 注释,一个来自javax.faces.bean 包,用于用@ManagedBean 注释的JSF 托管bean,另一个来自javax.faces.view 包,用于用@Named 注释的CDI 托管bean。当 bean 作用域注解与 bean 管理注解不匹配时,那么实际的 bean 作用域将成为 bean 管理框架的默认作用域,在 JSF 托管 bean 中为 @RequestScoped,在 CDI 托管 bean 中为 @Dependent

    您需要确保您拥有以下任一构造并且不要混合使用它们,另请参阅@ViewScoped bean recreated on every postback request when using JSF 2.2。

     import javax.faces.bean.ManagedBean;
     import javax.faces.bean.ViewScoped;
    
     @ManagedBean
     @ViewScoped
     public class CorrectJSFViewScopedBean implements Serializable 
    
     import javax.inject.Named;
     import javax.faces.view.ViewScoped;
    
     @Named
     @ViewScoped
     public class CorrectCDIViewScopedBean implements Serializable 
    

    视图(意外地?)通过&lt;f:view transient="true"&gt; 标记为瞬态。这基本上开启了“无状态 JSF”,这是自 Mojarra 2.1.19 以来的新功能。因此,JSF 视图根本不会保存在 JSF 状态中,逻辑结果是所有引用的视图范围 bean 不能再与 JSF 视图相关联。另见What is the usefulness of statelessness in JSF?

    Web 应用程序配置了 com.sun.faces.enableRestoreView11Compatibility 上下文参数设置为 true 以错误地尝试“避免”ViewExpiredException。使用此上下文参数,ViewExpiredException 将永远不会被抛出,但视图(以及所有关联的视图范围 bean)只会从头开始重新创建。但是,如果每个请求都发生这种情况,那么这种方法实际上隐藏了另一个问题:视图过期太快了。这表明在维护 JSF 视图状态和/或 HTTP 会话时可能存在问题。如何正确解决/配置该问题,请前往javax.faces.application.ViewExpiredException: View could not be restored。

    Web 应用程序的运行时类路径被多个不同版本的 JSF API 或 impl 相关类污染。这会导致 JSF 视图状态的标识符/标记出现损坏/不匹配。您需要确保 webapp 的 /WEB-INF/lib 中没有多个 JSF API JAR 文件。如果您使用 Maven,请确保将服务器提供的库标记为 &lt;scope&gt;provided&lt;/scope&gt;。另请参阅our JSF wiki page 中的“安装 JSF”部分以及此相关问题的答案:How to properly install and configure JSF libraries via Maven?。

    当您使用 PrimeFaces &lt;p:dialog&gt; 时,请确保 &lt;p:dialog&gt; 有自己的 &lt;h:form&gt;,并且它没有嵌套在另一个 &lt;h:form&gt; 中。另见p:fileUpload inside p:dialog losing @ViewScoped values。

    当您将 PrimeFaces FileUploadFilter 与 PrettyFaces 结合使用时,请确保 FileUploadFilter 也在 PrettyFaces 重写/转发的请求上运行。另见ViewScoped bean rebuilt when FileUploadListener called using PrettyFaces 和How to use PrimeFaces p:fileUpload? Listener method is never invoked or UploadedFile is null / throws an error / not usable。

    当您使用 PrettyFaces 时,将 CSS/JS/图像资源重定向到与 @ViewScoped bean 绑定的 JSF 页面的错误配置的重写规则也会产生误导行为。另见CDI ViewScope & PrettyFaces: Multiple calls to @PostConstruct (JSF 2.2)。

【讨论】:

你的正确。我在我的页面上使用 c:foreach 的网站添加了一个功能。难怪我之前没有注意到。问题是 ui:repeat 在我的情况下不起作用。 primefaces.prime.com.tr/forum/viewtopic.php?f=3&t=59 部分状态保存是否适用于 c:for each? 很遗憾,不,禁用部分状态保存并不能解决 JSTL 标签的问题。然而,这种特殊情况很棘手。从理论上讲,您可以使用一个完整的组件来解决这个问题,例如&lt;p:tabs&gt; 接受集合而不是 &lt;ui:repeat&gt;&lt;p:tab&gt;。我相信这曾经是作为 PrimeFaces 的功能请求发布的,不确定 Cagatay 对它做了什么。 PARTIAL_STATE_SAVING 改变了我的生活 @Danijel:只使用rendered 属性?另见***.com/a/4870557 和***.com/a/15947948。或者,只需升级到 Mojarra 2.1.18 或更高版本。从那以后它就被修复了。 谢谢。使用 ...非空 bean.list">

以上是关于@ViewScoped 在每个回发请求上调用 @PostConstruct的主要内容,如果未能解决你的问题,请参考以下文章

将 GET 请求参数放入 @ViewScoped bean

JSF ViewScoped bean 在所有 ajax 请求中的唯一 ID?

p:remoteCommand 销毁 @ViewScoped 托管 bean

JSF 2.2 ViewScoped Bean 被多次创建

JSF 2.x @ViewScoped 托管 bean 线程安全吗?

为啥我的 javascript 调用回发并且我没有错误?