Spring Framework is a powerful tool for building Java applications, providing a comprehensive programming and configuration model. A key concept in Spring is the management of beans. In this detailed guide, we’ll explore the bean lifecycle in Spring, discussing the phases, interfaces, and examples to illustrate how beans are created, configured, and destroyed.
In Spring, a bean is simply an object that is instantiated, assembled, and managed by the Spring IoC (Inversion of Control) container. These beans are typically created from classes that are defined in the Spring configuration.
The lifecycle of a Spring bean can be broken down into several distinct phases:
Let’s delve into each phase in detail.
The lifecycle begins with the instantiation of the bean. This is where the Spring container creates an instance of the bean class.
Example:
@Bean
public MyBean myBean() {
return new MyBean();
}
In the example above, MyBean
is created when the Spring container processes this configuration.
Once the bean is instantiated, the Spring container populates its properties. This is done via dependency injection, where dependencies are set using setter methods or constructor arguments.
Example:
public class MyBean {
private Dependency dependency;
@Autowired
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
}
After the properties are set, the Spring container calls the setBeanName
method of the BeanNameAware
interface (if implemented by the bean). This allows the bean to know its name within the Spring container.
Example:
public class MyBean implements BeanNameAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
}
}
Next, the setBeanFactory
method of the BeanFactoryAware
interface is called. This method gives the bean a reference to the BeanFactory
that created it.
Example:
public class MyBean implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
}
Before the initialization phase, the Spring container applies any BeanPostProcessors
. These are special interfaces that allow you to modify the bean instance before and after the initialization callbacks.
Example:
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// Logic before initialization
return bean; // return the bean (can modify)
}
}
After the pre-initialization phase, the bean is initialized. If the bean implements InitializingBean
, the afterPropertiesSet()
method is called. Additionally, any custom initialization methods specified in the configuration (via the @PostConstruct
annotation or init-method
attribute) are invoked.
Example:
public class MyBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// Custom initialization logic
}
}
Or using @PostConstruct
:
public class MyBean {
@PostConstruct
public void init() {
// Initialization logic
}
}
After the bean is fully initialized, the Spring container applies BeanPostProcessors
again. This allows for further modifications or enhancements after the initialization phase.
Example:
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// Logic after initialization
return bean; // return the bean (can modify)
}
}
Finally, when the application context is closed, the container calls the destroy
method on any beans that implement DisposableBean
. If the bean has a custom destroy method specified via the @PreDestroy
annotation or destroy-method
attribute, that method is invoked as well.
Example:
public class MyBean implements DisposableBean {
@Override
public void destroy() {
// Cleanup logic
}
}
Or using @PreDestroy:
public class MyBean {
@PreDestroy
public void cleanup() {
// Cleanup logic
}
}
Now that we’ve covered the lifecycle phases, let’s put it all together in a complete Spring application example.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
public class MyBean implements BeanNameAware, DisposableBean {
private String beanName;
@Autowired
private Dependency dependency;
@PostConstruct
public void init() {
System.out.println("MyBean is initialized");
}
@PreDestroy
public void cleanup() {
System.out.println("MyBean is destroyed");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name is set: " + name);
}
@Override
public void destroy() {
System.out.println("DisposableBean destroy method called");
}
}
public class Dependency {
public void execute() {
System.out.println("Executing dependency logic");
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
@Bean
public Dependency dependency() {
return new Dependency();
}
@Bean
public MyBean myBean() {
return new MyBean();
}
}
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyBean myBean = context.getBean(MyBean.class);
context.close();
}
}