比如 两个 屏幕 的通信,一个改变另一个也改变,或者springbean 跟 屏幕 通信之类的
1 个赞
CUBA 中的全局事件是为了解决 web 和 core 模块之间通信的问题。现在 Jmix 不分这两个模块,也就没有这个功能了。
在 Jmix 中,从业务逻辑(bean)和持久层与 UI 进行通信是基于 Spring 的应用程序事件实现的解决方案。但是只能在单一服务端实例中使用。
一个简单的解决方案是使用框架提供的 UiEventPublisher
bean。这个 bean 可以往当前用户打开的浏览器 tab 中发送事件。例如,如果用户在一个浏览器 tab 中点击某个按钮,按钮调用发送事件的代码,然后事件会发送至那个 tab 中的界面。
创建 Event 类:
package com.company.app.entity;
import org.springframework.context.ApplicationEvent;
public class FooChangedEvent extends ApplicationEvent {
public FooChangedEvent(Object source) {
super(source);
}
}
在需要接收通知的界面内创建一个事件监听器。下面的代码是在主界面创建的:
public class MainScreen extends Screen implements Window.HasWorkArea {
// ...
@Autowired
private Notifications notifications;
@EventListener
private void fooChanged(FooChangedEvent event) {
notifications.create().withCaption("A Foo instance has been changed").show();
}
例如,当实体修改时,使用 UiEventPublisher
发送 event:
@Component
public class FooEventListener {
@Autowired
private UiEventPublisher uiEventPublisher;
@TransactionalEventListener
public void onFooChangedAfterCommit(EntityChangedEvent<Foo> event) {
uiEventPublisher.publishEvent(new FooChangedEvent(this));
}
}
复杂一点的情况,如果需要给当前所有用户的所有 tab 都发送 event,需要创建一个更加复杂的 publisher。下面是一个示例,这个 bean 可以注册服务端创建的所有 Vaadin 会话,对会话进行遍历并发送 event:
package com.company.app;
import com.vaadin.server.VaadinSession;
import io.jmix.ui.App;
import io.jmix.ui.AppUI;
import io.jmix.ui.event.AppInitializedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Component
public class GlobalUiEventPublisher {
private static final Logger log = LoggerFactory.getLogger(GlobalUiEventPublisher.class);
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final List<WeakReference<VaadinSession>> sessions = new ArrayList<>();
@EventListener
public void onAppStart(AppInitializedEvent event) {
lock.writeLock().lock();
try {
sessions.add(new WeakReference<>(VaadinSession.getCurrent()));
} finally {
lock.writeLock().unlock();
}
}
public void publishEvent(ApplicationEvent event) {
ArrayList<VaadinSession> activeSessions = new ArrayList<>();
int removed = 0;
lock.readLock().lock();
try {
for (Iterator<WeakReference<VaadinSession>> iterator = sessions.iterator(); iterator.hasNext(); ) {
WeakReference<VaadinSession> reference = iterator.next();
VaadinSession session = reference.get();
if (session != null) {
activeSessions.add(session);
} else {
lock.readLock().unlock();
lock.writeLock().lock();
try {
iterator.remove();
lock.readLock().lock();
} finally {
lock.writeLock().unlock();
}
removed++;
}
}
} finally {
lock.readLock().unlock();
}
if (removed > 0) {
log.debug("Removed {} Vaadin sessions", removed);
}
log.debug("Sending {} to {} Vaadin sessions", event, activeSessions.size());
for (VaadinSession session : activeSessions) {
// obtain lock on session state
session.access(() -> {
if (session.getState() == VaadinSession.State.OPEN) {
// active app in this session
App app = App.getInstance();
// notify all opened web browser tabs
List<AppUI> appUIs = app.getAppUIs();
for (AppUI ui : appUIs) {
if (!ui.isClosing()) {
// work in context of UI
ui.accessSynchronously(() -> {
ui.getUiEventsMulticaster().multicastEvent(event);
});
}
}
}
});
}
}
}
下面是使用这个 publisher 的示例:
@Component
public class FooEventListener {
@Autowired
private GlobalUiEventPublisher globalUiEventPublisher;
@TransactionalEventListener
public void onFooChangedAfterCommit(EntityChangedEvent<Foo> event) {
globalUiEventPublisher.publishEvent(new FooChangedEvent(this));
}
}
好的,容我 这边试试,感觉应没啥问题,感谢解答(回复的有点晚见谅)
1 个赞
没事,我们有时候也会回复比较晚 能帮到你就好
OK,成功了,一开始没用上面这个类,并且一开始推送的事件是在屏幕中直接推送的,导致的事件未触发
有个问题,就是我用
@Component
public class FooEventListener {
@Autowired
private UiEventPublisher uiEventPublisher;
@TransactionalEventListener
public void onFooChangedAfterCommit(EntityChangedEvent<Foo> event) {
uiEventPublisher.publishEvent(new FooChangedEvent(this));
}
}
发送的XXX实体变更的事件,然后在 XXX实体的页面监听如下:
@EventListener
private void gameLibChanged(GameLibChangedEvent event) {
gameLibsDl.load();
推送事件与监听都正常,可是监听端数据容易并未刷新,请问有什么方法解决么,也就是好像刷新这个数据容器的时候数据库并未更新