baeldung - Coding and Testing Stuff: Guide To Running Logic on Startup in Spring

Datetime:2016-08-22 23:33:37          Topic: Spring  Test Engineer           Share

The Master Class of " Learn Spring Security " is out:

>> CHECK OUT THE COURSE

1. Introduction

In this article we’ll focus on how to run logic at the startup of a Spring application .

2. Running Logic On Startup

Running logic during/after Spring application’s startup is a common scenario, but one that causes multiple problems.

In order to benefit from Inverse of Control, we naturally need to renounce partial control over the application’s flow to the container – which is why instantiation, setup logic on startup, etc needs special attention.

We can’t simply include our logic in the beans’ constructors or call methods after instantiation of any object; we are simply not in control during those processes.

Let’s look at the real-life example:

@Component
public class InvalidInitExampleBean {

    @Autowired
    private Environment env;

    public InvalidInitExampleBean() {
        env.getActiveProfiles();
    }
}

Here, we’re trying to access an autowired field in the constructor. When the constructor is called, the Spring bean is not yet fully initialized. This is problematic because calling not yet initialized fields will of course result in  NullPointerException s .

Spring gives us a few ways of managing this situation.

2.1. The  @PostConstruct Annotation

Javax’s @PostConstruct annotation can be used for annotating a method that should be run once immediately after the bean’s initialization . Keep in mind that the annotated method will be executed by Spring even if there is nothing to inject.

Here’s @PostConstruct in action:

@Component
public class PostConstructExampleBean {

    private static final Logger LOG = Logger.getLogger(PostConstructExampleBean.class);

    @Autowired
    private Environment environment;

    @PostConstruct
    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

In the example above you can see that the Environment instance was safely injected and then called in the @PostConstruct annotated method without throwing a NullPointerException .

2.2. The  InitializingBean Interface

The InitializingBean approach works pretty similarly to the previous one. Instead of annotating a method, you need to implement the  InitializingBean interface and the  afterPropertiesSet() method.

Here you can see the previous example implemented using the InitializingBean interface:

@Component
public class InitializingBeanExampleBean implements InitializingBean {

    private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.3. An ApplicationListener

This approach can be used for running logic after the Spring context has been initialized , so we are not focusing on any particular bean, but waiting for all of them to initialize.

In order to achieve this you need to create a bean that implements the ApplicationListener<ContextRefreshedEvent> interface:

@Component
public class StartupApplicationListenerExample implements 
  ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOG = Logger.getLogger(StartupApplicationListenerExample.class);

    public static int counter;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

The same results can be achieved by using the newly-introduced @EventListener annotation:

@Component
public class EventListenerExampleBean {

    private static final Logger LOG = Logger.getLogger(EventListenerExampleBean.class);

    public static int counter;

    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

In this example we chose the ContextRefreshedEvent. Make sure to pick an appropriate event that suits your needs.

2.4. The XML init-method

The Init-method is an XML-way of executing a method after a bean’s initialization.

Here is what a bean looks like:

public class InitMethodExampleBean {

    private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);

    @Autowired
    private Environment environment;

    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

You can notice that there is no special interfaces implemented nor any special annotations used.

And this is how a bean definition looks in an XML config:

<bean id="initMethodExampleBean"
  class="org.baeldung.startup.InitMethodExampleBean"
  init-method="init">
</bean>

2.5. Constructor Injection

If you are injecting fields using Constructor Injection, you can simply include your logic in a constructor:

@Component 
public class LogicInConstructorExampleBean {

    private static final Logger LOG = Logger.getLogger(LogicInConstructorExampleBean.class);

    private final Environment environment;

    @Autowired
    public LogicInConstructorExampleBean(Environment environment) {
        this.environment = environment;
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

3. Combining Mechanisms

In order to achieve full control over your beans, you might want to combine the above mechanisms together.

The order of execution is as follows:

  1. The constructor
  2. the @PostConstruct annotated methods
  3. the InitializingBean’s afterPropertiesSet() method
  4. the initialization method specified as  init-method in XML

Let’s create a Spring bean that combines all mechanisms:

@Component
@Scope(value = "prototype")
public class AllStrategiesExampleBean implements InitializingBean {

    private static final Logger LOG = Logger.getLogger(AllStrategiesExampleBean.class);

    public AllStrategiesExampleBean() {
        LOG.info("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("InitializingBean");
    }

    @PostConstruct
    public void postConstruct() {
        LOG.info("PostConstruct");
    }

    public void init() {
        LOG.info("init-method");
    }
}

If you try to instantiate this bean, you will be able to see logs that match the order specified above:

[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
[main] INFO o.b.startup.AllStrategiesExampleBean - init-method

4. Conclusion

In this article we illustrated multiple ways of executing logic on Spring’s application startup.

Code samples can be found on GitHub .





About List