Tom Jenkinson
Tom Jenkinson

Reputation: 1480

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

Say I have the following:

Config

Sets up the guava EventBus bean.

@Configuration
public class AppConfig {

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

Event listener

Listens and handles to some/all of the events that are put on the event bus. May have several different classes similar to this that subscribe to events in the application.

@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");
    }
}

Event Dispatcher

Something that puts events on the event bus.

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

Main App Entry Point

@ComponentScan
@EnableAutoConfiguration
public class Application {

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

How can I make sure that all of the classes that are listening to events are instantiated before the classes that post them? Ie at the moment it could be possible for the event dispatcher class to be instantiated and post the event before the listener is instantiated, meaning the listener would have missed the events.

I was thinking maybe creating another annotation like @EventListener, and then have spring prioritise all classes with this annotation somehow, but not sure how to implement this or if this is actually a good idea anyway.

Thanks.

Upvotes: 3

Views: 4492

Answers (1)

Tom Jenkinson
Tom Jenkinson

Reputation: 1480

I decided to extend the Guava EventBus class creating BufferedEventBus.

This simply buffers any event objects that are posted to it until the application context has finished loading. When this happens it then posts any buffered events. Any future events are posted immediately and not buffered.

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();
        }
    }

}

I am now simply using this instead of the standard EventBus throughout my application.

Upvotes: 3

Related Questions