Running code on Spring Boot startup

Spring Boot does a lot of configuration automatically for us but sooner or later you’ll have to do some custom work. In this post, you will learn how to hook into the application bootstrap lifecycle and execute code on Spring Boot startup.

So let’s see what the framework has to offer.

Advertisement

1. Execute method on bean initialization

The simplest way to run some logic once the Spring starts your application is to execute the code as part of a chosen bean bootstrapping process.

What you have to do?

Just create a class, mark it as a Spring component, and put the app initialization code in a method with the @PostConstruct annotation. In theory, you could use a constructor instead of a separate method but it’s a good practice to separate the object’s construction from its real responsibility.

@Component
class AppInitializator {

    private static final Logger log = LoggerFactory.getLogger(AppInitializator.class);

    @PostConstruct
    private void init() {
        log.info("AppInitializator initialization logic ...");
        // ...
    }

}

If you use the lazy initialization of the application context (e.g. to speed up Spring Boot startup), the bean with initialization logic should be excluded from this mechanism. I’ve described how to create a bean eagerly with the @Lazy annotation when whole Spring context uses lazy loading.

You can also create a method with the @PostConstruct annotation inside your main Spring Boot application class. Don’t forget that the main class is also a component managed by the framework.

@SpringBootApplication
public class InitDemoApplication {

    // ...

    @PostConstruct
    private void init() {
        log.info("InitDemoApplication initialization logic ...");
        // ...
    }

}

But this solution feels like a workaround rather than a real solution. You can control the order in which the Spring framework creates application beans in a very limited way. If we want to run the initialization logic before all beans are created or even before the framework starts, we need to find something better.

2. Spring Boot startup hooks

The beauty of applications created with Spring Boot is that the only thing you need to run them is Java Runtime Environment and the command line interface. No external tools or application required. It runs just like a regular Java SE program.

As every Java program, you start execution of such application in the static main method of your entry application class. That’s the point in which you can hook into Spring Boot initialization process.

2.1. Creating Spring Boot hook

Start by changing the code in your main method to extract appending of startup hooks to a separate method. You should add Spring Boot hooks before the application is started.

public static void main(String[] args) {
    SpringApplication application = new SpringApplication(InitDemoApplication.class);
    addInitHooks(application);
    application.run(args);
}

static void addInitHooks(SpringApplication application) {
    // TBD …
}

When a Spring Boot application starts, it publishes several events on individual steps of the bootstrap process. The API of the SpringApplication class exposes a method which we can use to add listeners for those events.

Here’s an example which runs a startup method on the event published before the Spring context starts creating your beans:

static void addInitHooks(SpringApplication application) {
   application.addListeners((ApplicationListener<ApplicationEnvironmentPreparedEvent>) event -> {
       String version = event.getEnvironment().getProperty("java.runtime.version");
       log.info("Running with Java {}", version);
   });
}

2.2. Event types

Depending on the event type, the object which Spring passes to the listener may give you access to several useful operations. In the previous example, we read some environment property but we could also modify it if needed.

Here’s the list of possible events sorted by the order in which events are published by Spring Boot on the startup:

  • ApplicationStartingEvent
  • ApplicationEnvironmentPreparedEvent
  • ApplicationContextInitializedEvent
  • ApplicationPreparedEvent
  • ApplicationStartedEvent
  • ApplicationReadyEvent

I don’t want to duplicate the documentation of events so if you’re interested in the description you should check it out. There is also ApplicationFailedEvent but it’s only published when the framework fails to start your application.

From my experience, the most important event is ApplicationEnvironmentPreparedEvent. At this moment of the Spring Boot startup, the beans aren’t created yet but you can access the whole application configuration. Usually, that’s the best moment to run some custom startup code.

3. Run code on startup without embedded Tomcat

Although Spring Boot designers created the framework with building fat JARs in mind, some developers still deploy Spring Boot applications to regular servlet containers like Tomcat. If that is the case for you, the solution from the previous paragraph won’t work without an additional step.

If you deploy your application as a regular WAR file, you probably already have a custom implementation of the SpringBootServlerInitializator. You only have to slightly extend it and add your initialization hooks as a part of the application building process.

You can easily reuse the addInitHooks() method we created in the main application class.

public class InitDemoWarInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        InitDemoApplication.addInitHooks(builder.application());
        return builder.sources(InitDemoApplication.class);
    }

}

Conclusion

In short, there are two main options to run code on Spring Boot startup. The simplest one is rather designed to initialize a particular bean. For more global cases, the framework has a dedicated solution to hook into its lifecycle using event listeners. We learned how to implement and set up such listeners.

I hope you find the post useful. You can find the fully working demo in my Github repository. I would be happy to see your comments about the use cases in which a custom hook was required. That should make interesting reading.

Facebooktwittergoogle_plusredditlinkedinmail
Advertisement