Jmix UI控制器如何订阅非UI控制器里发布的事件?

总所周知,界面控制器里是无法订阅spring的applicationContext.publishEvent(event);事件的,它只支持EventHub.fireEvent(Class<E> eventType, E event) ;发布的事件订阅,而EventHub的作用域只在abstract class Screen里,也就固定了只能在界面控制器上使用。

现在想要在后台Service里发布一个事件,对应的界面控制器里可以订阅到。请问如何实现?或者这样隔离的设计目的是什么?如果过强制实现该功能会不会出现bug?

请参考 Jmix 还有全局事件这个功能么 - Jmix - jmix.cn

image

我这也有一个方案,可以评估一下

我是按照 Screen里的 eventHub 的事件订阅机制改造的。

思路很简单,让 Screen里的 eventHub在业务bean里可访问既可以发送事件了。

实现:
监听ScreenOpenedEvent事件,每次打开窗口通过反射获取eventHub


    @EventListener
    public void screenOpenedEventListener(ScreenOpenedEvent event) {
        Screen source = event.getSource();

        //获取class对象
        Class<?> clazz = source.getClass();
        //获取当前对象中声明的方法
        Method method = null;
        try {
            method = clazz.getDeclaredMethod("getEventHub");
        } catch (NoSuchMethodException e) {
            // throw new RuntimeException(e);
        }

        if (method != null) {

            method.setAccessible(true);

            try {
                eventHub = (EventHub) method.invoke(source);
            } catch (IllegalAccessException | InvocationTargetException e) {
                // throw new RuntimeException(e);
            }

        }

    }

然后再定一个发送方法

    public static <E> void fireEvent(E event) {
        if (eventHub != null) {
            eventHub.publish(((Class<E>) event.getClass()), event);
        }
    }

在UI控制器里

1注册订阅方法

    @Override
    public EventHub getEventHub() {
        // 反射获取EventHub用
        return super.getEventHub();
    }

    // 注册订阅(要 add 或 set 开头)
    public Subscription addEventListener1(Consumer<DeviceOnlineEvent> listener) {
        return getEventHub().subscribe(DeviceOnlineEvent.class, listener);
    }


2添加订阅方法

@Subscribe
    public void deviceOnlineHandler(DeviceOnlineEvent event) {
        String deviceId = event.getDeviceId();
        boolean online = event.isOnline();

        Device device = devices.get(deviceId);
        if (device != null) {
            systemAuthenticator.runWithSystem(() -> device.setStatus(online ? Status.ONLINE : Status.OFFLINE));
        }
    }