
本文介绍在不依赖第三方消息队列的前提下,通过 httpsession 共享数据、利用 session-scoped event queue 主动通知 zk viewmodel 的两种标准方案,并提供可落地的代码示例与关键注意事项。
本文介绍在不依赖第三方消息队列的前提下,通过 httpsession 共享数据、利用 session-scoped event queue 主动通知 zk viewmodel 的两种标准方案,并提供可落地的代码示例与关键注意事项。
在 ZK 框架中,Servlet 层(如 HttpServlet)与 ZK MVVM 层天然处于不同执行上下文:Servlet 运行在标准 Java EE 容器线程中,而 ZK ViewModel 依赖于 ZK 的 Execution 和 Desktop 生命周期。因此,直接调用 ViewModel 方法或触发桌面级事件(如 DesktopEventQueue)不可行——因为 HttpServlet 无法获取当前 Execution 或 Desktop 实例。
✅ 方案一:通过 HttpSession 共享状态数据(推荐用于简单、非实时场景)
这是最轻量、最可靠的方式。Servlet 将处理结果存入当前 HTTP Session,ZK ViewModel 在初始化或响应用户操作时读取该属性:
// 在 Servlet 中(例如 doPost)
HttpSession session = request.getSession(true);
session.setAttribute("userNotification", "订单已成功提交,预计30分钟内发货");
session.setAttribute("orderStatus", "CONFIRMED");
response.sendRedirect("zkui.zul"); // 跳转至 ZK 页面// 在 ZK ViewModel 中(使用 @NotifyChange 或 @NotifyChange("status"))
public class OrderViewModel {
private String notification;
private String status;
@Init
public void init(@ContextParam(ContextType.SESSION) HttpSession session) {
this.notification = (String) session.getAttribute("userNotification");
this.status = (String) session.getAttribute("orderStatus");
// 清理避免重复消费(可选)
session.removeAttribute("userNotification");
session.removeAttribute("orderStatus");
}
// getter/setter...
}⚠️ 注意:
@ContextParam(ContextType.SESSION)是 ZK 8+ 的标准注入方式;若使用旧版,可通过Sessions.getCurrent().getNativeSession()获取。
✅ 方案二:通过 Session-scoped Event Queue 主动推送事件(适用于需实时响应的场景)
当需要从 Servlet 主动触发 ViewModel 中的逻辑(如弹出提示、刷新列表),应使用 SessionEventQueue。它不依赖 Execution,仅需有效的 HttpSession ID 即可投递事件:
// 在 Servlet 中发送事件
HttpSession session = request.getSession();
String sessionId = session.getId();
EventQueue<Event> queue = EventQueues.lookup("zkSessionEvents", EventQueue.SESSION, true);
queue.publish(new Event("onOrderProcessed", null,
new HashMap<String, Object>() {{
put("orderId", "ORD-2024-7890");
put("result", "SUCCESS");
}}
));<!-- zkui.zul -->
<zk>
<window apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('OrderViewModel')">
<label value="@load(vm.message)" />
<!-- 绑定事件监听 -->
<div onNotify="@command('handleOrderEvent', data=event.data)" />
</window>
</zk>// ViewModel 中监听事件
public class OrderViewModel {
private String message = "";
@NotifyChange("message")
@Command
public void handleOrderEvent(@BindingParam("data") Map<String, Object> data) {
String orderId = (String) data.get("orderId");
String result = (String) data.get("result");
this.message = "订单 " + orderId + " 处理完成:" + result;
}
}? 关键点:
EventQueues.lookup("zkSessionEvents", EventQueue.SESSION, true)创建的是会话级队列,ZK 自动将其绑定到当前HttpSession;Servlet 侧只需确保使用同一 session ID(通常通过 Cookie 或重定向维持),即可实现跨层通信。
总结与建议
- 优先选用 Session 属性共享:适用于跳转后展示结果、表单提交反馈等常见场景,零配置、高兼容、易调试。
- 谨慎使用 Session Event Queue:虽支持主动通知,但需确保事件消费逻辑幂等(如防止重复刷新),且需在 ZK 页面加载时已注册监听器。
- ❌ 避免尝试
DesktopEventQueue或ApplicationEventQueue:前者要求Execution上下文(Servlet 不具备),后者为全局广播,缺乏会话隔离性,易引发并发问题。 - 最终选择应基于业务实时性需求——若用户操作后需立即感知结果,用事件队列;若为页面跳转后的状态呈现,用 Session 属性更简洁稳健。

















