ADF中JSF页面检测浏览器关闭事件并调用后端Java方法求助
解决ADF Faces页面关闭时触发后端方法的问题
嘿,我明白你现在遇到的麻烦——想在用户关闭浏览器标签/窗口时触发后端Java方法,但ADF的serverListener没响应,同时还要给用户确认提示。咱们一步步拆解问题,搞定它!
先说说你原代码里的核心问题
- 事件绑定错误:你在
af:document里用了onunload="onbeforeunload",这完全不对,应该绑定onbeforeunload事件,而不是onunload。 - 异步请求被浏览器终止:
AdfCustomEvent.queue是异步操作,但现代浏览器在beforeunload事件中会快速终止大部分异步请求,还没等请求发到后端,页面就关了,所以serverListener根本收不到事件。 - 自定义提示文本失效:现在Chrome、Firefox等主流浏览器都不允许自定义
beforeunload的提示文本了,只能显示浏览器默认的确认框,你设置的browser Is Closed !..不会显示出来。
解决方案:用可靠的方式触发后端请求 + 正确处理关闭提示
第一步:修改JS代码(BrowserEventClose.js)
用navigator.sendBeacon这个专门为页面卸载场景设计的API,它能保证请求被后台处理,还不会阻塞页面关闭。同时正确触发浏览器的默认确认提示:
window.addEventListener('beforeunload', function(event) { // 触发浏览器默认的关闭确认提示(自定义文本已不被支持) event.preventDefault(); event.returnValue = ''; // 非空值即可触发默认提示 // 准备要发送给后端的参数 const formData = new FormData(); formData.append('testParam', 'so'); // 发送请求到后端处理接口(替换成你的应用路径) navigator.sendBeacon('/your-app-context/close-handler', formData); });
第二步:修改JSF页面
修正af:document的事件绑定,去掉无效的serverListener(因为我们用sendBeacon直接请求后端):
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE html> <f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich"> <af:document title="testBrowserChrome.jsf" id="d1" clientComponent="true"> <af:form id="f1"> <af:resource type="javascript" source="resources/js/BrowserEventClose.js"/> </af:form> </af:document> </f:view>
第三步:后端处理请求(创建Servlet)
写一个Servlet来接收sendBeacon的请求,然后调用你的ServerTest bean方法:
import javax.faces.context.FacesContext; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/close-handler") public class CloseHandlerServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { // 获取前端传过来的参数 String testParam = request.getParameter("testParam"); // 获取你的ViewScope Bean并调用方法 ServerTest serverTest = (ServerTest) FacesContext.getCurrentInstance() .getApplication() .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{viewScope.ServerTest}", ServerTest.class); serverTest.testServerListener(testParam); // 返回成功状态 response.setStatus(HttpServletResponse.SC_OK); } }
如果你坚持要用ADF的ServerListener
如果不想用Servlet,也可以用同步AJAX请求(但会阻塞页面关闭,体验稍差),修改JS代码如下:
window.addEventListener('beforeunload', function(event) { event.preventDefault(); event.returnValue = ''; const docComponent = AdfPage.PAGE.findComponentByAbsoluteId('d1'); if (docComponent) { // 构建ADF自定义事件的同步AJAX请求 const request = new XMLHttpRequest(); const clientId = docComponent.getClientId(); // 同步请求(第三个参数为false) request.open('POST', window.location.pathname + '?javax.faces.partial.ajax=true', false); request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // 组装ADF事件参数 const params = `javax.faces.source=${clientId}&javax.faces.partial.execute=${clientId}&javax.faces.partial.render=${clientId}&javax.faces.event=test&test=so`; request.send(params); } });
此时你的JSF页面可以保留af:serverListener:
<af:serverListener type="test" method="#{viewScope.ServerTest.testServerListener}"/>
关键要点总结
beforeunload限制:现代浏览器不允许自定义提示文本,只能触发默认确认框;异步请求大概率会被终止,必须用navigator.sendBeacon或同步AJAX。navigator.sendBeacon优势:异步、不阻塞页面关闭、浏览器保证请求送达,是页面卸载场景的最佳选择。- ADF事件的局限性:异步的
AdfCustomEvent.queue在beforeunload里几乎无法生效,因为页面关闭速度远快于请求发送完成的速度。
内容的提问来源于stack exchange,提问作者mohamed essam




