본문 바로가기
Backend/Spring

Spring Bean Life Cycle / 스프링 빈 생명주기

by unknownomad 2023. 11. 7.

What is a Spring Bean?

A Spring bean is a Java object managed by the Spring IoC container.

 

Why Understand the Spring Bean Life Cycle?

A solid grasp of the bean life cycle empowers you to effectively manage resources, configure beans, and ensure proper initialization and cleanup. With this knowledge, you can optimize your application’s performance, prevent memory leaks, and implement custom logic at various stages of a bean’s existence.

 

Benefits of Exploring the Spring Bean Life Cycle

  • Resource Management
  • Customization
  • Dependency Injection
  • Debugging

 

Who Manages the Spring Bean Life Cycle?

In Spring applications, the IoC container is responsible for managing the life cycle of beans. The IoC container is the architectural backbone that handles the instantiation, configuration, and disposal of beans.

 

When Does the Spring Bean Life Cycle Begin?

The Spring Bean life cycle commences when the IoC container is initialized. As your application starts, the container springs to life, creating the foundation for the journey your beans are about to embark upon.

The Spring Bean life cycle begins when the IoC container starts up with your application. This initial step marks the start of the bean’s journey in the Spring framework.

 

Visualizing the Spring Bean Life Cycle

 

Spring Bean Life Cycle Stages

1. Instantiation: Setting the Foundation

Purpose:

  • Initialize essential properties and resources.
  • Set up the initial state of the bean.

When to Use:

  • Set initial values or configurations.

Example:

  • The instantiation stage initializes the bean and sets the foundation for its journey.
public class Character {
    public Character() { // INSTANTIATION
        System.out.println("Instantiation: A new character has been created.");
    }
}

 

2. Population of Properties: Filling the Gaps

Purpose:

  • Inject properties and dependencies into the bean.
  • Configure the bean with the required resources.

When to Use:

  • To populate bean properties from the context.

Example:

  • Beans equip themselves with dependencies during this stage.
  • The container injects these properties, ensuring beans are fully prepared for their roles.
public class Character {
    private Weapon weapon;
    private Item item;

    public void setWeapon(Weapon weapon) { // POPULATION OF PROPERTIES
        this.weapon = weapon;
        System.out.println("Population of Properties: Equipping " + weapon.getName() + " to " + getName());
    }

    public void setItem(Item item) { // POPULATION OF PROPERTIES
       this.item = item;
        System.out.println("Population of Properties: Adding " + item.getName() + " to " + getName() + "'s inventory");
    }
}

 

3. BeanNameAware: Giving Identity

Purpose:

  • Allow beans to know their assigned name.
  • Access the name assigned to the bean.

When to Use:

  • When the bean needs to reference itself within the context.

Example:

  • In Spring terms, the bean becomes aware of its assigned name within the container.
  • This gives the bean a sense of identity within the Spring ecosystem.
public class Character implements BeanNameAware {
    @Override
    public void setBeanName(String name) { // BEAN-NAME-AWARE
        System.out.println("BeanNameAware: Setting bean name: " + name);
    }
}

 

4. BeanFactoryAware and ApplicationContextAware: Embracing Context

Purpose:

  • Access the broader application context.
  • Interact with other beans and resources.

When to Use:

  • To access global context and resources.

Example:

  • During this stage, the bean becomes aware of the context it belongs to – a world where other beans and resources reside.
public class Character implements BeanFactoryAware, ApplicationContextAware {

    @Override
    public void setBeanFactory(BeanFactory beanFactory) { // BEAN-FACTORY-AWARE
        System.out.println("BeanFactoryAware: Setting bean factory");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) { // APPLICATION-CONTEXT-AWARE
        System.out.println("ApplicationContextAware: Setting application context.");
    }
}

 

5. BeanPostProcessor: Adding Magic

Purpose:

  • Execute custom logic before and after initialization.
  • Customize the bean’s behavior during creation.

When to Use:

  • For adding custom behavior during bean creation.

Example:

  • This is the time to perform custom logic just before and after a bean’s initialization – an enchantment that transforms your bean into something extraordinary.
public class MagicBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) { // BEAN-POST-PROCESSOR
        if (bean instanceof Character) {
            System.out.println("BeanPostProcessor: Adding a touch of magic to " + ((Character) bean).getName());
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) { // BEAN-POST-PROCESSOR
        if (bean instanceof Character) {
            System.out.println("BeanPostProcessor: Magic continues for " + ((Character) bean).getName());
        }
        return bean;
    }
}

 

6. @PostConstruct: Customizing Bean Initialization

Purpose:

  • Execute custom logic after a bean has been initialized.

When to Use:

  • Customization: Utilize @PostConstruct when you want to add your own custom behavior to a bean after its creation.
  • Final Setup: It’s useful when you need to perform specific actions on the bean right after it’s initialized, such as configuring properties or performing last-minute preparations.

Example:

  • Might need to perform some readiness checks.
import jakarta.annotation.PostConstruct;

public class Character {
    
    @PostConstruct // POST-CONSTRUCT
    public void init() {
         System.out.println("@PostConstruct: " + getName() + " is preparing for action.");
    }
}

 

7. Initializing Bean: Preparing for Action

Purpose:

  • Perform custom setup and initialization logic.
  • Execute actions after properties are set.

When to Use:

  • When additional setup beyond the constructor is needed.

Example:

  • During the initialization stage, your beans are getting ready for their mission.
  • This is where you can execute custom setup logic after properties are set, ensuring your beans are fully prepared for action.
public class Character implements InitializingBean {
    @Override
    public void afterPropertiesSet() { // INITIALIZING-BEAN
        System.out.println("Initialization: " + getName() + " is undergoing intensive training.");
    }
}

 

8. Custom Initialization: Tailored Setup

Purpose:

  • Perform unique setup tasks for the bean.
  • Execute specialized initialization logic.

When to Use:

  • When specific initialization is required.

Example:

  • Beans can undergo personalized preparation during this stage.
  • Custom initialization methods allow beans to gear up for their roles in unique ways.
public class Character {
    public void customInit() { // CUSTOM-INITIALIZATION
        System.out.println("Custom Initialization: Executing custom init for " + getName());
        performSpecialTraining();
    }
    
    private void performSpecialTraining() {
        System.out.println("Custom Initialization: " + getName() + " is performing a special training routine.");
    }
}

 

9. @PreDestroy: Preparing for Cleanup

Purpose:

  • Implement custom cleanup operations before the destruction of the bean.
  • Ensure proper resource release and perform any necessary cleanup tasks.

When to Use:

  • When you need to execute specific cleanup logic just before a bean is destroyed.
  • To ensure that resources, such as open files or database connections, are properly released.

Example:

import jakarta.annotation.PreDestroy;

public class Character {

    @PreDestroy // PRE-DESTROY
    public void preDestroyCleanup() {
        System.out.println("@PreDestroy: " + getName() + " is saying goodbye and preparing to rest.");
    }
}

 

10. DisposableBean: Bidding Farewall

Purpose:

  • Implement cleanup operations before destruction.
  • Ensure proper resource release and cleanup.

When to Use:

  • To perform cleanup actions before destruction.

Example:

  • During the DisposableBean stage, beans wrap up their journey.
  • This is the moment for final actions and cleanup before the bean’s exit.
public class Character implements DisposableBean {

    @Override
    public void destroy() { // DISPOSABLE-BEAN
        System.out.println("DisposableBean: " + getName() + " is saying goodbye and resting.");
        restAndRecover();
    }
    
    private void restAndRecover() {
        System.out.println("DisposableBean: " + getName() + " is resting and recovering energy.");
    }

}

 

11. Custom Destruction: Parting Moments

Purpose:

  • Execute custom logic before destruction.
  • Perform specialized cleanup actions.

When to Use:

  • When unique cleanup is necessary.

Example:

public class Character {

    public void customDestroy() { // CUSTOM-DESTRUCTION
       System.out.println("Custom Destruction: " + getName() + " is bidding farewell and performing a final action.");
        sayGoodbye();
        performFinalAction();
    }
    
    private void sayGoodbye() {
        System.out.println("Custom Destruction: " + getName() + " says goodbye.");
    }

    private void performFinalAction() {
        System.out.println("Custom Destruction: " + getName() + " performs a final action.");
    }
}

 

Source Code Output

 

Practice Cases

  • Use Constructor Initialization: Whenever possible, initialize bean properties through constructors. Constructor injection promotes immutability and ensures that a bean is in a valid state from the moment it’s created.
  • Avoid Excessive Customization: While custom initialization and destruction methods (e.g., @PostConstruct and @PreDestroy) are valuable, use them judiciously. Overly complex custom logic can make your application harder to maintain.
  • Leverage Annotations: Take advantage of annotations like @PostConstruct and @PreDestroy for defining initialization and destruction methods. They provide a clear and concise way to manage the life cycle.
  • Use InitializingBean and DisposableBean Sparingly: Interfaces like InitializingBean and DisposableBean should be used only when your beans require specific initialization or cleanup logic. In most cases, annotations or custom methods are preferred for clarity.
  • Favor @Bean Initialization: When creating beans, favor Java-based configuration with @Bean methods within @Configuration classes. This approach allows for centralized configuration and clear bean management.
  • Keep Initialization Lightweight: Initialization logic should be lightweight and focused on setting up the bean’s state. Heavy operations, like database connections, should be performed lazily to avoid unnecessary resource consumption.
  • Be Mindful of Dependencies: Understand the order in which beans are initialized when they have dependencies on each other. Use @DependsOn or constructor injection to ensure that dependencies are available when needed.
  • Use Bean Scopes Wisely: Choose the appropriate bean scope (singleton, prototype, etc.) based on the use case. Singleton beans have a longer life cycle, while prototype beans are created each time they are requested.
  • Consider Bean Post-Processors: BeanPostProcessors can be powerful tools for customizing bean behavior. However, use them cautiously, as they can complicate the bean life cycle.
  • Test Life Cycle Behavior: Write unit tests to validate that beans are being initialized and destroyed as expected. This ensures that your configuration and life cycle management are correct.
  • Document Custom Life Cycle Logic: If you use custom initialization or destruction methods, document their purpose and when they should be called. This helps maintainers understand the intended behavior.
  • Monitor Resource Usage: Keep an eye on resource usage during initialization and destruction, especially in applications with many beans. Excessive resource consumption can impact application performance.

 

Reference:

https://bootcamptoprod.com/spring-bean-life-cycle-explained/

댓글