1. Overview

In this tutorial, we'll investigate the life cycle methods for a Spring bean. In essence, we'll create methods and configure Spring so that it'll call these methods after the bean construction and before the bean destruction.

2. Life Cycle Management using Annotations

Firstly, we'll look at how we can hook into a bean's life cycle using annotations.

For this purpose, Spring supports the @PostConstruct and @PreDestroy annotations. Note that these annotations aren't part of the Spring Framework and reside in the javax.annotation package. Since it doesn't tie our application to Spring-specific interfaces, using @PostContstruct and @PreDestroy is the recommended approach for receiving lifecycle callbacks:

@Component
public class AnnotationBasedService {

    @PostConstruct
    public void init(){
        System.out.println("Initializing " + getClass().getSimpleName());
    }

    @PreDestroy
    public void destroy(){
        System.out.println("Destroying " + getClass().getSimpleName());
    }
}

Here, we've annotated the init() method with @PostConstruct. As a result, Spring calls this method after the bean is created. Similarly, Spring calls the @PreDestroy annotated method before it destroys the bean.

3. Life Cycle Management using @Bean

Secondly, we'll define life cycle methods using the @Bean annotation.

The @Bean annotation has initMethod and destroyMethod attributes which we can use:

@Component
public class CustomMethodsService {

    public void initialize() throws Exception {
        System.out.println("Initializing " + getClass().getSimpleName());
    }

    public void destroy() {
        System.out.println("Destroying " + getClass().getSimpleName());
    }
}

Here, we have a regular component, CustomMethodsService. Note that it doesn't have any annotation except @Component.

Let's configure it using @Bean:

@Configuration
public class BeanConfiguration {

    @Bean(destroyMethod = "destroy", initMethod = "initialize")
    public CustomMethodsService withCustomMethodsService() {
        return new CustomMethodsService();
    }
}

Here we're assigning the initMethod and destroyMethod attributes with our custom methods.

In this approach, similar to the previous one, we aren't relying on Spring-specific classes to receive lifecycle callbacks.

4. Life Cycle Management using Inheritance

Now, let's look at the inheritance-based approach.

Firstly, Spring provides InitializingBean interface which includes the afterPropertiesSet() method. Spring calls this method after the bean construction.

Secondly, Spring provides DisposableBean interface which includes the destroy() method. As the name implies, Spring calls this method before the bean destruction.

@Component
public class InheritingService implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializing " + getClass().getSimpleName());
    }

    @Override
    public void destroy() {
        System.out.println("Destroying " + getClass().getSimpleName());
    }
}

Here, we have the InheritingService class which implements both InitializingBean and DisposableBean.

Since we're using Spring's own interfaces - InitializingBean and DisposableBean - it needlessly couples our application with Spring. Thus it isn't preferable considering the previous approaches.

5. Multiple Life Cycle Methods

Lastly, let's see how Spring behaves when we have multiple life cycle methods using different approaches.

We have the MixedService class:

public class MixedService implements InitializingBean, DisposableBean {

    @PostConstruct
    public void annotationInit() {
        System.out.println("Initializing since @PostConstruct");
    }

    @PreDestroy
    public void annotationDestroy() {
        System.out.println("Destroying since @PreDestroy");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Destroying since DisposableBean");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializing since InitializingBean");
    }

    public void customInit() {
        System.out.println("Initializing since init-method");
    }

    public void customDestroy() {
        System.out.println("Initializing since destroy-method");
    }
}

Then we have the bean configuration:

@Configuration
public class BeanConfiguration {

    @Bean(destroyMethod = "customDestroy", initMethod = "customInit")
    public MixedService mixedService() {
        return new MixedService();
    }
}

When Spring creates a bean of MixedService, it'll first call the @PostConstruct method, secondly, InitializingBean's init() method and lastly the custom initialization method. The destroy-related methods also follow a similar order.

6. Summary

In this tutorial, we've investigated life cycle methods and how to configure Spring to run them.

Check out the source code for all examples in this article over on Github.