8

I need to set some system properties programmatically and I figured that the best way would be doing it in the event listener once ApplicationEnvironmentPreparedEvent was intercepted. But the problem is that I am not able to catch that event in my listener.

@Component
public class AppListener {

    @EventListener
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", MyThreadFactory.class.getName());
        }
    }

}

What am I doing wrong and why I cannot catch that event?

Ihor M.
  • 2,728
  • 3
  • 44
  • 70

2 Answers2

15

From the Spring Boot documentation:

Some events are actually triggered before the ApplicationContext is created, so you cannot register a listener on those as a @Bean. You can register them with the SpringApplication.addListeners(…​) method or the SpringApplicationBuilder.listeners(…​) method.

And from the Spring documentation (this for version 5.1.7):

Processing of @EventListener annotations is performed via the internal EventListenerMethodProcessor bean which gets registered automatically when using Java config or manually via the or element when using XML config.

So ApplicationEnvironmentPreparedEvent (and all of the Spring Boot application events) occur before context and bean creation, and @EventListener is controlled by a bean, so the @EventListener annotation cannot be picked up at this point. You will have to specifically create a listener class and add it explicitly to capture this event (or any event that occurs before bean creation).

Your class:

public class AppListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>
{
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event)
    {
        System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory",
                MyThreadFactory.class.getName());
    }
}

...and the relevant SpringApplicationBuilder code:

ConfigurableApplicationContext context = new SpringApplicationBuilder(Launcher.class)
            .listeners(new AppListener()).run();
Jason
  • 886
  • 8
  • 11
  • I have this setup but i do have a problem with `spring.jpa` and `datasource`. When I run the application using the embedded tomcat, the listener works as expected and properties are filled from aws but when i deploy the war to the tomcat, it does not call this listener and it goes directly to contextinitialization and after that `datasource`, and since datasource properties are not filled, i get exception on the jpa initialization step. what is the problem with this listener and tomcat? – Mohamad Eghlima Mar 03 '22 at 18:09
-3

You need implement ApplicationListener interface for this event specifically

@Component
public class AppListener implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", MyThreadFactory.class.getName());
        }

    }
}
lozanoco
  • 37
  • 1