在 spring 中,确保在派发事件之前实例化 Guava Event Bus 侦听器 bean

In spring make sure Guava Event Bus listener beans are instantiated before events are dispatched

假设我有以下内容:

配置

设置 guava EventBus bean。

@Configuration
public class AppConfig {

    @Bean
    public EventBus eventBus() {
        return new EventBus(); // guava event bus
    }
}

事件侦听器

监听并处理事件总线上的 [​​=45=] 个事件。 可能有几个不同的 class 与订阅应用程序中的事件类似。

@Component
public class EventListener {

    @Autowired
    public EventListener(EventBus eventBus) {
        eventBus.register(this); // register this instance with the event bus so it receives any events
    }

    @Subscribe
    public void onTestEvent(TestEvent e) {
        System.out.println("Caught event");
    }
}

事件调度程序

将事件放在事件总线上的东西。

@Component
public class EventDispatcher {
    @Autowired
    public EventDispatcher(EventBus eventBus) {
        eventBus.post(new TestEvent());
    }
}

主应用入口点

@ComponentScan
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

如何确保所有正在侦听事件的 classes 在 post 它们的 classes 之前实例化?也就是说,目前事件调度器 class 可能会被实例化,而事件 post 事件可能会在侦听器被实例化之前发生,这意味着侦听器可能会错过事件。

我在想也许可以创建另一个注解,比如@EventListener,然后让 spring 以某种方式优先使用这个注解的所有 classes,但不确定如何实现这个或者这是否真的是一个好主意。

谢谢。

我决定扩展 Guava EventBus class 创建 BufferedEventBus

这只是缓冲发送给它的任何事件对象,直到应用程序上下文完成加载。当发生这种情况时,它会发布任何缓冲的事件。任何未来事件都会立即发布,不会被缓冲。

BufferedEventBus

package tjenkinson.events;

import java.util.ArrayList;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;

import com.google.common.eventbus.EventBus;

/**
 * A guava event bus that holds back any events that are posted to it until the application
 * context has finished loading meaning all eager-loaded beans will have been constructed.
 * 
 * This means any eager-loaded beans that want to listen to events on this event bus will be able
 * to and not miss any events that were posted from other beans before they had a chance to listen.
 */

@Service
public class BufferedEventBus extends EventBus implements ApplicationListener<ContextRefreshedEvent> {

    private boolean applicationContextLoaded = false;
    private final ArrayList<Object> bufferedEvents = new ArrayList<>();

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        synchronized(bufferedEvents) {
            if (applicationContextLoaded) {
                // context already loaded. maybe it's been refreshed.
                return;
            }

            postBufferedEvents();
            applicationContextLoaded = true;
        }
    }

    @Override
    public void post(Object event) {
        synchronized(bufferedEvents) {
            if (applicationContextLoaded) {
                super.post(event);
            }
            else {
                bufferedEvents.add(event);
            }
        }
    }

    private void postBufferedEvents() {
        synchronized(bufferedEvents) {
            for (Object event : bufferedEvents) {
                super.post(event);
            }
            bufferedEvents.clear();
        }
    }

}

我现在只是在整个申请中使用它而不是标准 EventBus