secondbreakfast
secondbreakfast

Reputation: 4372

ContextStartedEvent not firing in custom listener

I am trying to hook into the creation of the context using a custom application listener like this

@Component
public class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {

    @Override
    public void onApplicationEvent(ContextStartedEvent event) {
        System.out.println("Context started"); // this never happens
    }
}

But the onApplicationEvent method never fires. If I use a different event such as ContextRefreshedEvent then it works just fine, but I need to hook into before it is created. Any advice? Thanks!

Upvotes: 14

Views: 10114

Answers (2)

kaqqao
kaqqao

Reputation: 15429

Answer by Phil Webb (from the Spring team) on a similar issue on their GitHub:

The ContextStartedEvent is being published, but it's before any beans are registered so it's already happened by the time your ContextStartedListener @Component is created. You can use SpringApplication.addListeners if you want to listen for early events.

Upvotes: 2

dimitrisli
dimitrisli

Reputation: 21391

[Edit]

Editing answer adding more info because of the downvote.

The reason why you are not getting a callback by the listener is because you are not explicitly calling the LifeCycle start() method (JavaDoc).

This cascades down to your ApplicationContext normally via the AbstractApplicationContext on in Spring Boot case via the ConfigurableApplicationContext.

Example of working code below demonstrating how your callback would work (just explicitly call the start() method)

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
        applicationContext.start();
    }

    @Component
    class ContextStartedListener implements ApplicationListener<ContextStartedEvent> {

        @Override
        public void onApplicationEvent(ContextStartedEvent event) {
            System.out.println("Context started");
        }
    }
}

The reason why I suggested below the ContextRefreshedEvent callback instead is because behind the scenes the refresh() code is getting invoked.

If you drill down the SpringApplication#run() method you'll eventually see it.

Again here's a working example of how this would work using the ContextRefreshedEvent:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@SpringBootApplication
public class DemoApplication {

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

    @Component
    class ContextStartedListener implements ApplicationListener<ContextRefreshedEvent> {

        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            System.out.println("Context refreshed");
        }
    }
}

[Before Edit]

Change the Generic type to ContextRefreshedEvent instead and then it should work.

For more details read this article from the Spring Blog. Just to quote the part about the ContextRefreshedEvent:

[..]This allows MyListener to be notified when the context has refreshed and one can use that to run arbitrary code when the application context has fully started.[..]

Upvotes: 26

Related Questions