4

I am trying to publish a custom event in Spring MVC, but is is not firing while context is loading, below are the code snippet,

The onConnectionOpened will be called after connecting to a server which is triggered after bean initialization using @PostConstruct

@Autowired
private ApplicationEventPublisher publisher;

public void onConnectionOpened(EventObject event) {
    publisher.publishEvent(new StateEvent("ConnectionOpened", event));

}

I am using annotation in listener part as below

@EventListener
public void handleConnectionState(StateEvent event) {
   System.out.println(event);
}

I am able to see events fired after the context is loaded or refreshed, is this expected that custom application events can be published after the context loaded or refreshed?.

I am using Spring 4.3.10

Thanks in advance

java_dev
  • 323
  • 6
  • 17
  • 1
    The `@EventListener` is processed when the context is fully initialized. If you want to catch events really early (if you can even publish them) use an `ApplicationListener` instead of an `@EventListener`. – M. Deinum Nov 13 '17 at 19:56
  • ApplicationListener is working thanks Deinum – java_dev Nov 13 '17 at 20:12

2 Answers2

7

The @EventListener annotations are processed by the EventListenerMethodProcessor which will run as soon as all beans are instantiated and ready. As you are publishing an event from a @PostConstruct annotated method it might be that not everything is up and running at that moment and @EventListener based methods haven't been detected yet.

Instead what you can do is use the ApplicationListener interface to get the events and process them.

public class MyEventHandler implements ApplicationListener<StateEvent> {

    public void onApplicationEvent(StateEvent event) {
        System.out.println(event);
    }
}       
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • Very nice solution to the ops problem. Only drawback: when using ApplicationListener StateEvent needs to be a subclass of ApplicationEvent. https://stackoverflow.com/a/41281728/1501375 shows an approach with @EventListener and without the need for ApplicationEvent. – Kai Sep 19 '18 at 05:48
  • That won't work here (see the answer) as the events are fired too early and the `@EventListener` annotations haven't been processed. – M. Deinum Sep 19 '18 at 05:49
  • Well, it would work if the call to `onConnectionOpened` would be in the `@EventListener` for `ContextRefreshedEvent`. In that case `publishEvent` would happen later than `@PostConstruct`, wouldn't it? – Kai Sep 19 '18 at 05:58
  • True but that wasn't the question that was asked. – M. Deinum Sep 19 '18 at 06:02
  • Excellent finding. In my case im firing and catching an event as soon as the application is started. However the listener is not called (as you pointed out, not all the beans are ready). The same type of event is fired again very late in the application cycle (after 10 mins). So i was thinking there is a problem with the configuration. Now Im firing the first event only when all the beans are ready, with the help of ApplicationListener. This solved my problem. thanks! – Pragalathan M Nov 19 '19 at 11:38
0

You should publish event after ContextRefreshedEvent occur, but if you wait ContextRefreshedEvent in @PostConstruct, it will make whole applicaiton hang up, so use @Async will solve this problem.

@EnableAsync
@SpringBootApplication
public class YourApplication
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}
@Slf4j
@Service
public class PublishHelper {
    private final ApplicationEventPublisher publisher;

    private final CountDownLatch countDownLatch = new CountDownLatch(1);

    @EventListener(classes = {ContextRefreshedEvent.class})
    public void eventListen(ContextRefreshedEvent contextRefreshedEvent) {
        log.info("publish helper is ready to publish");
        countDownLatch.countDown();
    }

    public PublishHelper(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    @Async
    @SneakyThrows
    public void publishEvent(Object event) {
        countDownLatch.await();
        publisher.publishEvent(event);
    }
}
Zheng Xiaodong
  • 143
  • 3
  • 9