Running Code on Spring Boot Startup

Running Code on Spring Boot Startup

7 Comments

One of the things I love about the Grails environment is that it comes with a handy bootstrap.groovy file. This will run at startup of the Spring container. I frequently use this to setup expected data, or to create test data for integration / functional tests. It can be a very convenient way to seed an H2 database with startup values.

The Spring Framework itself does not have the concept for a bootstrap file like Grails does. It does, however, have events that we can subscribe to and functionally accomplish the same thing.

Spring Framework Events

The Spring Framework comes out of the box with a number of events, and you’re able to extend the event functionality for your own purposes.

Spring Core Events

ContextRefreshedEvent

This event is published whenever the Spring Context is started or refreshed.

ContextStartedEvent

This event is published when the Spring Context is started.

ContextStoppedEvent

This event is published when the Spring Context is stopped. In practice, you will not use this event very often. It can be handy for doing cleanup work, like closing connections.

ContextClosedEvent

This event is similar to the ContextStoppedEvent, but in this case, the Context cannot be re-started.

Spring Boot Events

Spring Boot introduces several new events on top of the events available in the core Spring Framework.

ApplicationStartedEvent

This event is published early in the startup of a Spring Application. The Spring Context is running but may change later in the lifecycle.

ApplicationEnvironmentPreparedEvent

This event is published when the Spring Boot Application is starting up and is first available for inspection and modification.

ApplicationPreparedEvent

This event is published when the Spring Context is fully prepared but not refreshed. At this point, the Spring Beans are loaded, configured and ready for use.

ApplicationFailedEvent

This event is published when the Spring Boot Application fails to start. This event is useful for error logging or alerting.

Using Spring Framework Events

Under the scenario we want to do something on startup we have two events we can consider using. Traditionally under Spring Framework, we can use the ContextRefreshedEvent. This event has been around since the beginning of the Spring Framework.

If you’re using Spring Boot, you do have additional events to select from. I often want to use a startup event to seed data for tests, so in this case, I need the database connection to be set up. Reading about the Spring Boot Events, I thought the event I would like to use is ApplicationPreparedEvent. But in testing it out, this was not the case. I ran into some issues with getting the event listeners set up properly in the Spring Boot Context. I found better results using the ContextRefreshedEvent.

ContextRefreshedEvent Listener

Here is an example of a listener. Here, I’m injecting a simple bean to prove I got a message. In practice, this bean could be whatever you wanted. You could, for example, inject a Spring Data Repository into your listener bean.

ContextRefresehedListener.java

package guru.springframework.blog.contextrefresh;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent>{

    private EventHolderBean eventHolderBean;

    @Autowired
    public void setEventHolderBean(EventHolderBean eventHolderBean) {
        this.eventHolderBean = eventHolderBean;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println("Context Event Received");
        eventHolderBean.setEventFired(true);
    }
}

EventHolderBean.java

Nothing very special about this bean. I have an event fired property, which I initialize to false. If it is true, I know the bean ‘processed’ and event.

package guru.springframework.blog.contextrefresh;

import org.springframework.stereotype.Component;

@Component
public class EventHolderBean {
    private Boolean eventFired = false;

    public Boolean getEventFired() {
        return eventFired;
    }

    public void setEventFired(Boolean eventFired) {
        this.eventFired = eventFired;
    }
}

Running the Event Bean in Spring Boot

I can run this bean in a Spring Boot Application. Below is my application class.

ContextRefreshedApplication.java

package guru.springframework.blog.contextrefresh;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class ContextRefresehedApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(ContextRefresehedApplication.class, args);
        EventHolderBean bean = ctx.getBean(EventHolderBean.class);
        System.out.println("Event Processed?? - " + bean.getEventFired());
    }
}

Output

In the output, you can see my console messages.

   .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.3.RELEASE)

2015-06-12 17:24:47.158  INFO 14158 --- [           main] g.s.b.c.ContextRefresehedApplication     : Starting ContextRefresehedApplication on Johns-MacBook-Pro.local with PID 14158 (/Users/jt/src/springframework.guru/blogposts/target/classes started by jt in /Users/jt/src/springframework.guru/blogposts)
2015-06-12 17:24:47.217  INFO 14158 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@e874448: startup date [Fri Jun 12 17:24:47 EDT 2015]; root of context hierarchy
2015-06-12 17:24:48.484  INFO 14158 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
Context Event Received
2015-06-12 17:24:48.564  INFO 14158 --- [           main] g.s.b.c.ContextRefresehedApplication     : Started ContextRefresehedApplication in 1.804 seconds (JVM running for 2.454)
Event Processed?? - true
2015-06-12 17:24:48.566  INFO 14158 --- [       Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@e874448: startup date [Fri Jun 12 17:24:47 EDT 2015]; root of context hierarchy
2015-06-12 17:24:48.567  INFO 14158 --- [       Thread-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

Process finished with exit code 0

Testing the Spring Event Bean

I can also set this up in a JUnit test. This will actually run outside of Spring Boot and in a normal Spring context.

To do this I need to set up a simple Java configuration bean for my test.

ContextRefreshConfig.java

package guru.springframework.blog.contextrefresh.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("guru.springframework.blog.contextrefresh")
public class ContextRefreshConfig {
}

ContextRefresehedListenerTest.java

Here I have a simple JUnit test which brings up the Spring Context and gets an instance of the Event Holder Bean. I check to make sure the event fired is set to true, proving the bean did, in fact, get manipulated by the event listener.

package guru.springframework.blog.contextrefresh;

import guru.springframework.blog.contextrefresh.config.ContextRefreshConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ContextRefreshConfig.class})
public class ContextRefreshedListenerTest {

    private EventHolderBean eventHolderBean;

    @Autowired
    public void setEventHolderBean(EventHolderBean eventHolderBean) {
        this.eventHolderBean = eventHolderBean;
    }

    @Test
    public void testContext(){
         assertTrue(eventHolderBean.getEventFired());
    }
}

Test Output

In the output of the test, I can see the console output of the event listener.

 17:32:18.902 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
17:32:18.903 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'contextRefreshedListener'
Context Event Received
17:32:18.905 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Searching for key 'spring.liveBeansView.mbeanDomain' in [systemProperties]
17:32:18.906 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Searching for key 'spring.liveBeansView.mbeanDomain' in [systemEnvironment]

Free Introduction to Spring Tutorial

Are you new to the Spring Framework? Checkout my Free Introduction to Spring Online Tutorial.

Get The Code

I’ve committed the source code for this post to github. It is a Maven project which you can download and build. If you wish to learn more about the Spring Framework, I have a free introduction to Spring tutorial. You can sign up for this tutorial in the section below.

Source Code

The source code for this post is available on github. You can download it here.

About jt

    You May Also Like

    7 comments on “Running Code on Spring Boot Startup

    1. March 28, 2017 at 5:37 pm

      Great article John. I came across it because I am needing to bootstrap my database in a Spring Boot project. I also found this https://coderwall.com/p/wfmxga/bootstrapping-data-with-spring and was wondering what approach I should take? I mean in terms of best practices.

      Reply
      • March 28, 2017 at 5:46 pm

        I’d favor this approach, since its tapping into a context event. With the initializing bean, the framework may still be finalizing other beans. Might not matter, but I could see it leading to random failures.

        Reply
    2. September 20, 2017 at 2:03 pm

      Hi,

      I have a question. Requesting your help.
      We have a spring boot application and i have written integration tests without Mocks with TestRestTemplate and @SpringbootTest.

      So on local machine, when i execute the tests, they execute fine as i have given MyApplication.class inside @Springboottest
      It will startup the spring application context and execute the tests.

      Till here everything is fine.

      But we deploy this application on different test environments like qa,e2e,staging and then on production.
      So we have to execute the Jenkins Job for my integration tests against the above environments as an acceptance tests.

      My Question over here is : – When i execute these tests on jenkins, the tests get executed on a Jenkins Slave machine(which is picked randomly among the available executors) and it will hit the end points (either qa or e2e or staging or production end points) and send rest requests and get the responses and validate.
      But the tests start up the application context on the jenkins slave and loads on a random port and will be available on the jenkins slave machine till the tests finish though i am not at all interacting with the application context( as i am hitting external test end points). Is there any option not to load the spring application context when i am trying to run tests against real test server and to load the application context when testing on local?

      Please help.. I am kind of new to spring boot and got stuck here.

      Thank you very much in advance

      Thanks,
      Ravi Shankar

      Reply
      • September 20, 2017 at 2:09 pm

        Yes – you can do this with Spring. I’d probably use profiles to control the configuration of the tests.

        This is completely off topic of this blog post. If you have further questions about the general useage of Spring, please post on Stackoverflow. Thanks!

        Reply
    3. May 16, 2018 at 1:19 am

      Thanks for such a nice explanation.

      Please enlighten me which is the better approach either to implement ApplicationListener or using Annotation-driven event listener? Like:

      @EventListener
      public void handleContextRefresh(ContextRefreshedEvent event) {
      …..
      }

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.