基本上,您希望在窗口卸载期间销毁 JSF 视图状态和所有视图范围的 bean。该解决方案已实施于OmniFaces@ViewScoped注解 http://showcase.omnifaces.org/cdi/ViewScoped它在其文档中充实如下:
在某些情况下,可能需要立即销毁视图作用域 bean 以及浏览器unload
事件被调用。 IE。当用户通过 GET 导航离开或关闭浏览器选项卡/窗口时。这两个 JSF 2.2 视图范围注释都不支持这一点。从 OmniFaces 2.2 开始,此 CDI 视图范围注释将保证@PreDestroy
带注释的方法也会在浏览器卸载时调用。这个技巧是通过自动包含的帮助程序脚本的同步 XHR 请求来完成的omnifaces:unload.js
。然而,有一个小警告:在慢速网络和/或较差的服务器硬件上,卸载页面的最终用户操作和所需结果之间可能存在明显的滞后。如果这是不可取的,那么最好坚持 JSF 2.2 自己的视图范围注释并接受推迟的销毁。
自 OmniFaces 2.3 以来,卸载得到了进一步改进,在服务器端状态保存的情况下,还可以从 JSF 实现的内部 LRU 映射中物理删除关联的 JSF 视图状态,从而进一步降低以下风险:ViewExpiredException
关于之前创建/打开的其他视图。作为此更改的副作用,@PreDestroy
在与 OmniFaces CDI 视图作用域 bean 相同的视图中引用的任何标准 JSF 视图作用域 bean 的带注释方法也将保证在浏览器卸载时被调用。
您可以在这里找到相关的源代码:
- 卸载脚本初始化程序:ViewScopeManager#registerUnloadScript() https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/java/org/omnifaces/cdi/viewscope/ViewScopeManager.java#L205
- 卸载脚本本身:unload.unminified.js https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/resources/META-INF/resources/omnifaces/unload.unminified.js
- 卸载视图处理程序:OmniViewHandler#unloadView() https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/java/org/omnifaces/viewhandler/OmniViewHandler.java#L127
- JSF 视图状态驱逐舰:Hacks#removeViewState() https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/java/org/omnifaces/util/Hacks.java#L417
卸载脚本将运行during https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/resources/META-INF/resources/omnifaces/unload.unminified.js#L61视窗beforeunload
event, unless这是由任何基于 JSF (ajax) 的表单提交引起的。至于 commandlink 和/或 ajax 提交,这是特定于实现的。现在 https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/resources/META-INF/resources/omnifaces/util.unminified.js#L71Mojarra、MyFaces 和 PrimeFaces 均得到认可。
卸载脚本将trigger https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/resources/META-INF/resources/omnifaces/unload.unminified.js#L72 navigator.sendBeacon https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon在现代浏览器上并回退到同步 XHR(异步会失败,因为页面可能会在请求实际到达服务器之前卸载)。
var url = form.action;
var query = "omnifaces.event=unload&id=" + id + "&" + VIEW_STATE_PARAM + "=" + encodeURIComponent(form[VIEW_STATE_PARAM].value);
var contentType = "application/x-www-form-urlencoded";
if (navigator.sendBeacon) {
// Synchronous XHR is deprecated during unload event, modern browsers offer Beacon API for this which will basically fire-and-forget the request.
navigator.sendBeacon(url, new Blob([query], {type: contentType}));
}
else {
var xhr = new XMLHttpRequest();
xhr.open("POST", url, false);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("Content-Type", contentType);
xhr.send(query);
}
卸载视图处理程序将明确地 https://github.com/omnifaces/omnifaces/blob/2.6.6/src/main/java/org/omnifaces/viewhandler/OmniViewHandler.java#L133摧毁所有@ViewScoped
beans,包括标准 JSF 的(请注意,仅当视图引用至少一个 OmniFaces 时,才会初始化卸载脚本@ViewScoped
bean).
context.getApplication().publishEvent(context, PreDestroyViewMapEvent.class, UIViewRoot.class, createdView);
然而,这不会破坏 HTTP 会话中的物理 JSF 视图状态,因此如下所示use case https://github.com/omnifaces/omnifaces/issues/208会失败:
- 将物理视图数设置为 3(在 Mojarra 中,使用
com.sun.faces.numberOfLogicalViews
上下文参数并在 MyFaces 中使用org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION
上下文参数)。
- 创建一个引用标准 JSF 的页面
@ViewScoped
bean.
- 在选项卡中打开此页面并始终保持打开状态。
- 在另一个选项卡中打开同一页面,然后立即关闭该选项卡。
- 在另一个选项卡中打开同一页面,然后立即关闭该选项卡。
- 在另一个选项卡中打开同一页面,然后立即关闭该选项卡。
- 在第一个选项卡中提交表单。
这会失败ViewExpiredException
因为之前关闭的选项卡的 JSF 视图状态在PreDestroyViewMapEvent
。他们仍然留在会议中。 OmniFaces@ViewScoped
实际上会摧毁他们。然而,销毁 JSF 视图状态是特定于实现的。这至少解释了其中相当hacky的代码Hacks
应该实现这一目标的类。
此特定案例的集成测试可以在ViewScopedIT#destroyViewState() https://github.com/omnifaces/omnifaces/blob/2.6.6/src/test/java/org/omnifaces/test/cdi/viewscoped/ViewScopedIT.java#L178 on ViewScopedIT.xhtml https://github.com/omnifaces/omnifaces/blob/2.6.6/src/test/resources/org.omnifaces.test.cdi.viewscoped/ViewScopedIT.xhtml这是现在 https://github.com/omnifaces/omnifaces/blob/2.6.6/pom.xml#L74针对 WildFly 10.0.0、TomEE 7.0.1 和 Payara 4.1.1.163 运行。
简而言之:只需更换javax.faces.view.ViewScoped
by org.omnifaces.cdi.ViewScoped
。其余部分是透明的。
import javax.inject.Named;
import org.omnifaces.cdi.ViewScoped;
@Named
@ViewScoped
public class Bean implements Serializable {}
我至少已经努力了propose http://download.oracle.com/javaee-archive/javaserverfaces-spec-public.java.net/jsr372-experts/2016/07/1091.html用于物理破坏 JSF 视图状态的公共 API 方法。也许它会出现在 JSF 2.3 中,然后我应该能够消除 OmniFaces 中的样板文件Hacks
班级。一旦在 OmniFaces 中完善,它可能最终会出现在 JSF 中,但不会早于 2.4。