LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1217 min read

Bean Lifecycle

Follow a Spring bean from creation to destruction, and learn how to hook into that lifecycle with @PostConstruct and @PreDestroy.

Introduction

A Spring bean does not simply appear fully formed. The container walks it through a well-defined sequence of steps: instantiate the object, inject its dependencies, run any initialization logic, keep it available for use, and eventually tear it down. Understanding this lifecycle lets you run setup code, like opening a connection, and cleanup code, like closing it, at exactly the right moment.

What You Will Learn
  • The phases a bean passes through
  • How to run code right after dependency injection with @PostConstruct
  • How to run cleanup code before a bean is destroyed with @PreDestroy
  • How to specify init and destroy methods for classes you cannot annotate

The Lifecycle at a Glance

For a singleton bean, the container follows this order: it calls the constructor, injects the required dependencies, invokes any @PostConstruct method, and the bean is then ready to use for the lifetime of the application context. When the context is closed, the container calls any @PreDestroy method before the bean is discarded.

  • 1. Constructor is called and the object is instantiated.
  • 2. Dependencies are injected (constructor, setter, or field injection).
  • 3. The method annotated with @PostConstruct runs.
  • 4. The bean is fully initialized and ready to be used.
  • 5. When the context shuts down, the method annotated with @PreDestroy runs.

Initialization with @PostConstruct

@PostConstruct marks a method that should run once, right after the bean's dependencies have been set. It is the standard place to put initialization logic that depends on injected fields being available, something a constructor cannot always guarantee with setter or field injection.

DatabaseConnector.java
@Component
public class DatabaseConnector {
@PostConstruct
public void connect() {
System.out.println("Connecting to the database...");
}
}
Console Output

Click Run to see what this code prints.

Destruction with @PreDestroy

@PreDestroy marks a method that runs just before the container discards the bean, giving you a chance to release resources such as open connections, threads, or file handles. Note that @PreDestroy only fires for singleton-scoped beans, since prototype beans are not tracked by the container after creation.

DatabaseConnector.java
@Component
public class DatabaseConnector {
@PostConstruct
public void connect() {
System.out.println("Connecting to the database...");
}
@PreDestroy
public void disconnect() {
System.out.println("Closing the database connection...");
}
}
Main.java
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext("com.example.app");
context.close();
Console Output

Click Run to see what this code prints.

Init and Destroy Methods on @Bean

You cannot add @PostConstruct or @PreDestroy to a class you do not own, such as one from a third-party library. In that case, declare the callbacks on the @Bean method instead, using the initMethod and destroyMethod attributes.

AppConfig.java
@Configuration
public class AppConfig {
@Bean(initMethod = "start", destroyMethod = "stop")
public CacheManager cacheManager() {
return new CacheManager();
}
}
Good to Know

For classes that implement java.io.Closeable or AutoCloseable, Spring will infer a "close" destroy method automatically, so you often do not even need to specify destroyMethod explicitly.

Common Mistakes

Avoid These Mistakes
  • Expecting @PreDestroy to run on prototype-scoped beans; the container does not manage their destruction.
  • Putting initialization logic that relies on injected fields inside the constructor instead of @PostConstruct.
  • Forgetting to call context.close() (or use a try-with-resources ConfigurableApplicationContext) in a standalone application, so destroy callbacks never fire.
  • Throwing unchecked exceptions from @PreDestroy methods, which can interrupt the shutdown of other beans.

Best Practices

  • Prefer @PostConstruct and @PreDestroy for classes you control; they keep lifecycle logic close to the class itself.
  • Use initMethod / destroyMethod on @Bean for third-party or legacy classes you cannot annotate.
  • Keep @PostConstruct methods fast; slow initialization delays application startup.
  • Always release external resources (connections, threads, file handles) in a destroy callback rather than relying on garbage collection.

Frequently Asked Questions

No, only one method per class should be annotated with @PostConstruct. If you need multiple setup steps, call them from within a single annotated method.

In modern Spring Boot applications they come from the jakarta.annotation package, which is included transitively. In plain Spring projects on older Java EE namespaces you may need to add the javax.annotation-api dependency.

No. It only runs during a graceful shutdown, such as calling context.close() or the JVM processing a registered shutdown hook. A forced kill (kill -9) skips it entirely.

Key Takeaways

  • A bean's life moves through instantiation, dependency injection, initialization, use, and destruction.
  • @PostConstruct runs once dependencies are injected and is the standard place for setup logic.
  • @PreDestroy runs before a singleton bean is discarded and is ideal for releasing resources.
  • For classes you cannot annotate, use @Bean(initMethod = ..., destroyMethod = ...) instead.
  • Prototype beans are not tracked for destruction once handed to the caller.

Summary

The bean lifecycle gives you predictable hooks for setup and teardown logic, so resources are acquired and released at the right time instead of being left to chance. With @PostConstruct, @PreDestroy, and the @Bean init/destroy attributes, you can manage almost any resource cleanly. Next, you will move from annotation-driven bean discovery toward explicitly wiring beans together using Java-based configuration classes.

Next Lesson →

Java-Based Configuration