0

0

Vaadin 14 中实现会话级销毁回调的正确方式

聖光之護

聖光之護

发布时间:2025-12-27 12:48:12

|

614人浏览过

|

来源于php中文网

原创

Vaadin 14 中实现会话级销毁回调的正确方式

在 vaadin 14 中,无法直接为单个 `session` 注册独立的 `sessiondestroylistener`,但可通过会话属性 + 全局监听器组合实现精准的会话销毁清理逻辑,避免误删其他活跃会话关联的数据。

Vaadin 的 VaadinService::addSessionDestroyListener 是全局注册机制——它只为整个服务(即应用)添加一个监听器,而非绑定到某个具体 Session。因此,像 ui.getSession().getService().addSessionDestroyListener(...) 这样的调用,无论在哪个 UI 实例中执行,最终都只会注册一次监听器,并在每个 Session 销毁时触发。这就是你观察到“一个会话销毁导致所有会话对应数据被清除”的根本原因:监听器是共享的,但内部逻辑未做会话上下文隔离。

✅ 正确做法是:将“需执行的动作”以数据或回调形式存入目标 Session,再由唯一的全局监听器读取并按需处理。以下是推荐实现方案:

1. 在会话中存储待清理数据(推荐用于简单场景)

// 在某个 UI 或组件初始化时(如 UI::init、@PostConstruct 或首次访问逻辑中)
UI.getCurrent().getSession().setAttribute("item-to-remove", this);

2. 全局注册一次 SessionDestroyListener(通常在 VaadinServiceInitListener 中)

@Component
public class SessionCleanupConfig implements VaadinServiceInitListener {
    @Override
    public void serviceInit(ServiceInitEvent event) {
        event.getSource().addSessionDestroyListener(event1 -> {
            // 安全获取当前即将销毁的 Session 中的待移除对象
            Object item = event1.getSession().getAttribute("item-to-remove");
            if (item != null) {
                // 注意:list 必须线程安全!Session 销毁可能并发发生
                list.remove(item);
            }
        });
    }
}

3. 处理并发安全(关键!)

由于多个 Session 可能同时销毁,list.remove(...) 若操作非线程安全集合(如 ArrayList),将引发 ConcurrentModificationException 或数据不一致。务必选用以下任一方式保护: