Spring Bean Lifecycle. How to use Spring IoC Container hooks

Oleksii Dushenin
4 min readJun 17, 2021

--

One of the benefits of Spring is an Inversion of Control (IoC) principle. In Spring objects are managed by Spring IoC container. These objects are called beans. Spring IoC container is responsible for instantiating, configuring, and assembling beans. As a result, a bean has a lifecycle that is managed by Spring. In some cases, bean behavior should be changed. It can be achieved by hooks provided by Spring Framework. In this post, the possible hooks will be reviewed.

Application Creation

Let’s start with Spring Initializr. In this post, we need a Spring Boot project without any dependencies.

After project generation, we will change our @SpringBootApplication class. CommandLineRunner interface will be used. It allows running the Spring Boot application as a task.

@SpringBootApplication
public class SpringBeanLifecycleApplication implements CommandLineRunner {

public static void main(String[] args) {
SpringApplication.run(SpringBeanLifecycleApplication.class, args);
}

@Override
public void run(String... args) {
System.out.println("SpringBeanLifecycleApplication started");
}

}

Method run from CommandLineRunner will be run after Spring initialization. It is expected to print SpringBeanLifecycleApplication message.

Bean Structure

In this small case study, two Spring beans will be created.

@Component
public class TestSpringBeanDependency {

public TestSpringBeanDependency() {
System.out.println("TestSpringBeanDependency constructor");
}

}

@Component
public class TestSpringBean {

private final TestSpringBeanDependency testSpringBeanDependency;

public TestSpringBean(TestSpringBeanDependency testSpringBeanDependency) {
this.testSpringBeanDependency = testSpringBeanDependency;
System.out.println("TestSpringBean constructor");
}

}

As you can see, two beans are defined:

  • TestSpringBeanDependency is a bean without any dependencies.
  • TestSpringBean is a bean with TestSpringBeanDependency.

Messages are added to the constructors.

@PostConstruct

Methods that are annotated with @PostConstruct are executed after Dependency Injection (DI) is done with initializations.

@Component
public class TestSpringBeanDependency {

public TestSpringBeanDependency() {
System.out.println("TestSpringBeanDependency constructor");
}

@PostConstruct
public void postConstruct() {
System.out.println("TestSpringBeanDependency postConstruct");
}

}

@Component
public class TestSpringBean {

private final TestSpringBeanDependency testSpringBeanDependency;

public TestSpringBean(TestSpringBeanDependency testSpringBeanDependency) {
this.testSpringBeanDependency = testSpringBeanDependency;
System.out.println("TestSpringBean constructor");
}


@PostConstruct
public void postConstruct() {
System.out.println("TestSpringBean postConstruct");
}

}

InitializingBean

Similarly to @PostConstruct, InitializingBean interface and it’s method afterPropertiesSet can be used to achieve the same results.

@Component
public class TestSpringBeanDependency implements InitializingBean {

public TestSpringBeanDependency() {
System.out.println("TestSpringBeanDependency constructor");
}

@Override
public void afterPropertiesSet() {
System.out.println("TestSpringBeanDependency afterPropertiesSet");
}

}



@Component
public class TestSpringBean implements InitializingBean {

private final TestSpringBeanDependency testSpringBeanDependency;
public TestSpringBean(TestSpringBeanDependency testSpringBeanDependency) {
this.testSpringBeanDependency = testSpringBeanDependency;
System.out.println("TestSpringBean constructor");
}

@Override
public void afterPropertiesSet() {
System.out.println("TestSpringBean afterPropertiesSet");
}

}

@PreDestroy

Method with @PreDestroy annotation is executed by Spring IoC Container before a bean is removed from the container.

@Component
public class TestSpringBeanDependency {

public TestSpringBeanDependency() {
System.out.println("TestSpringBeanDependency constructor");
}

@PreDestroy
public void preDestroy() {
System.out.println("TestSpringBeanDependency preDestroy");
}

}


@Component
public class TestSpringBean {

private final TestSpringBeanDependency testSpringBeanDependency;

public TestSpringBean(TestSpringBeanDependency testSpringBeanDependency) {
this.testSpringBeanDependency = testSpringBeanDependency;
System.out.println("TestSpringBean constructor");
}

@PreDestroy
public void preDestroy() {
System.out.println("TestSpringBean preDestroy");
}

}

DisposableBean

DisposableBean is an interface that is responsible to release resources on bean destruction. It is similar to @PreDestroy. Method destroy has to be implemented.

@Component
public class TestSpringBeanDependency implements DisposableBean {

public TestSpringBeanDependency() {
System.out.println("TestSpringBeanDependency constructor");
}

@Override
public void destroy() {
System.out.println("TestSpringBeanDependency destroy");
}

}

@Component
public class TestSpringBean implements DisposableBean {

private final TestSpringBeanDependency testSpringBeanDependency;

public TestSpringBean(TestSpringBeanDependency testSpringBeanDependency) {
this.testSpringBeanDependency = testSpringBeanDependency;
System.out.println("TestSpringBean constructor");
}

@Override
public void destroy() {
System.out.println("TestSpringBean destroy");
}

}

Beans With All Methods

The final version of our bean classes with all methods have the next form.

@Component
public class TestSpringBeanDependency implements DisposableBean {

public TestSpringBeanDependency() {
System.out.println("TestSpringBeanDependency constructor");
}

@Override
public void destroy() {
System.out.println("TestSpringBeanDependency destroy");
}

@Override
public void afterPropertiesSet() {
System.out.println("TestSpringBeanDependency afterPropertiesSet");
}

@PostConstruct
public void postConstruct() {
System.out.println("TestSpringBeanDependency postConstruct");
}

@PreDestroy
public void preDestroy() {
System.out.println("TestSpringBeanDependency preDestroy");
}

}

@Component
public class TestSpringBean implements DisposableBean {

private final TestSpringBeanDependency testSpringBeanDependency;

public TestSpringBean(TestSpringBeanDependency testSpringBeanDependency) {
this.testSpringBeanDependency = testSpringBeanDependency;
System.out.println("TestSpringBean constructor");
}

@Override
public void destroy() {
System.out.println("TestSpringBean destroy");
}

@Override
public void afterPropertiesSet() {
System.out.println("TestSpringBean afterPropertiesSet");
}

@PostConstruct
public void postConstruct() {
System.out.println("TestSpringBean postConstruct");
}

@PreDestroy
public void preDestroy() {
System.out.println("TestSpringBean preDestroy");
}

}

Bean Post Processor

Bean Post Processors play an important role in a Spring Bean Lifecycle. They allow custom modifications of new bean instances. E.g. beans can be wrapped with proxies.

BeanPostProcessor interface defines two methods:

  • postProcessBeforeInitialization — before initialization callbacks described in earlier sections.
  • postProcessAfterInitialization — after initialization callbacks described in earlier sections.
@Component
public class TestSpringBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (shouldOutput(beanName)) {
System.out.println(beanName + " TestSpringBeanPostProcessor postProcessBeforeInitialization");
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (shouldOutput(beanName)) {
System.out.println(beanName + " TestSpringBeanPostProcessor postProcessAfterInitialization");
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}

private boolean shouldOutput(String beanName) {
return "testSpringBeanDependency".equals(beanName) || "testSpringBean".equals(beanName);
}

}

We are only interested in two beans that we created earlier. As a result, messages are printed only for these beans.

Spring Bean Lifecycle

Let’s run SpringBeanLifecycleApplication from an IDE. Alternatively, you can run created jar file.

The order of method execution is the next one:

TestSpringBeanDependency constructor
testSpringBeanDependency TestSpringBeanPostProcessor postProcessBeforeInitialization
TestSpringBeanDependency postConstruct
TestSpringBeanDependency afterPropertiesSet
testSpringBeanDependency TestSpringBeanPostProcessor postProcessAfterInitialization
TestSpringBean constructor
testSpringBean TestSpringBeanPostProcessor postProcessBeforeInitialization
TestSpringBean postConstruct
TestSpringBean afterPropertiesSet
testSpringBean TestSpringBeanPostProcessor postProcessAfterInitialization
SpringBeanLifecycleApplication started
TestSpringBean preDestroy
TestSpringBean destroy
TestSpringBeanDependency preDestroy
TestSpringBeanDependency destroy

As you can see, the method execution order is the one that was described before.

Summary

In this post, Spring Bean Lifecycle was reviewed. Based on the business needs, we can hook into Spring Bean Lifecycle managed by Spring Ioc Container.

Typical operations are:

  • Set createdAt/updatedAt time.
  • Release resources.
  • Add proxy to the bean e.g. transactions support.

The source code is available at Github Spring Bean Lifecycle.

The Spring Boot Overview Course provides a high-level Spring Boot Application example.

Originally published at https://datamify.com on June 17, 2021.

--

--

Oleksii Dushenin
Oleksii Dushenin

Written by Oleksii Dushenin

IT Consultant with 8+ years of industry experience — https://datamify.com/

Responses (1)