What are the ways to run custom code when a Spring bean is initialized or destroyed?
answer
- Three mechanisms: annotation, interface, @Bean method
- @PostConstruct after injection, @PreDestroy on shutdown
- InitializingBean.afterPropertiesSet / DisposableBean.destroy
- jakarta.annotation package (Spring 6)
- prototypes: no destroy callback
basics
~10 sThree ways: annotate a method with @PostConstruct (init) or @PreDestroy (destroy); implement InitializingBean/DisposableBean interfaces; or set initMethod/destroyMethod on @Bean. Spring calls these automatically around the bean's life.
solid answer
~40 sSpring gives three mechanisms for lifecycle callbacks. First, JSR-250 annotations: a method marked @PostConstruct runs after dependency injection completes, and @PreDestroy runs before the bean is torn down. Second, the callback interfaces InitializingBean (afterPropertiesSet()) and DisposableBean (destroy()). Third, method names declared on the definition via @Bean(initMethod="...", destroyMethod="...") or the XML equivalent. The annotation approach is usually preferred because it's non-invasive (no Spring interface in your code) and works even when the class isn't yours only if you can annotate it; @Bean(initMethod/destroyMethod) is best for third-party classes you can't annotate. All three fire for singletons; destroy callbacks only fire when the container shuts down, and never for prototype-scoped beans.
code
java · 23 linesimport jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;
@Component
class CacheWarmer {
@PostConstruct
void warmUp() {
// runs after dependencies are injected
System.out.println("loading cache...");
}
@PreDestroy
void flush() {
// runs when the ApplicationContext closes
System.out.println("flushing cache...");
}
}
// Third-party class you can't annotate:
// @Bean(initMethod = "init", destroyMethod = "close")
// DataSource dataSource() { return new HikariDataSource(); }go deeper
Know the three mechanisms exist and what @PostConstruct/@PreDestroy do.
Know the exact packages, that destroy fires only on context close, and that @Bean(initMethod/destroyMethod) is for third-party classes.
Explain inferred destroyMethod, prototype non-destruction, and why annotations are preferred over interfaces.
Reason about post-processor registration (CommonAnnotationBeanPostProcessor), coupling trade-offs, and shutdown-hook lifecycle in different runtimes.
A **bean lifecycle callback** is code Spring runs at a defined moment in a bean's life: right after the bean is fully constructed and its dependencies injected (**initialization**), and right before the container discards it (**destruction**). Spring offers three independent mechanisms. **1. JSR-250 annotations — `@PostConstruct` / `@PreDestroy`** Annotate any no-argument, void method. `@PostConstruct` runs once, after the constructor has run and after all `@Autowired`/`@Value` dependencies have been set. `@PreDestroy` runs once, when the `ApplicationContext` is closing. These annotations live in the `jakarta.annotation` package (in Spring 6 / Spring Boot 3; older Spring 5 used `javax.annotation`). They are processed by the `CommonAnnotationBeanPostProcessor`, which is registered automatically in any annotation-driven context. Advantage: your class has no dependency on Spring types. **2. Callback interfaces — `InitializingBean` / `DisposableBean`** Implement `InitializingBean` and override `afterPropertiesSet()` for init; implement `DisposableBean` and override `destroy()` for teardown. These are the most 'coupled' option because your domain class now imports Spring interfaces. The Spring team generally discourages them for that reason, but they are slightly faster (no reflection to find the method). **3. Definition-level method names — `@Bean(initMethod, destroyMethod)`** On a `@Bean` factory method you name arbitrary methods: `@Bean(initMethod = "start", destroyMethod = "stop")`. Spring invokes them by reflection. This is the only option for **third-party classes** you cannot modify (e.g., a connection pool whose `close()` you want called on shutdown). `@Bean` also has **inferred destroy**: by default `destroyMethod` is the sentinel `"(inferred)"`, so if the bean class has a public `close()` or `shutdown()` method, Spring calls it automatically on shutdown. Set `destroyMethod = ""` to disable this inference. **Important semantics and gotchas** - **Destruction only happens on container shutdown.** In a web app the context closes when the app stops; in a standalone `main`, call `context.close()` or use a `try (var ctx = ...)` / register a JVM shutdown hook (`context.registerShutdownHook()`), otherwise `@PreDestroy` never fires. - **Prototype beans are never destroyed by Spring.** The container creates a prototype and hands it off; it does not track it, so `@PreDestroy`/`destroy()`/`destroyMethod` are **not** called on prototypes. Init callbacks *do* run for prototypes. - **Combining mechanisms on the same method runs it once.** If a method is both annotated `@PostConstruct` and named as `initMethod`, Spring detects it's the same method and invokes it a single time. - Methods should be **no-arg** (init/destroy method may throw). `@PostConstruct` methods returning a value or taking args are invalid. - Init callbacks run **after** injection, so it's the correct place to validate required collaborators or open resources; the constructor is too early because setters/field injection haven't run yet. **When to use which:** prefer `@PostConstruct`/`@PreDestroy` for your own code (clean, portable); use `@Bean(initMethod/destroyMethod)` for classes you don't own; reach for `InitializingBean`/`DisposableBean` only in framework-style code where the coupling is acceptable or you want to avoid annotation-processing overhead.
- Which package do @PostConstruct and @PreDestroy come from in Spring 6 / Boot 3?jakarta.annotation (they moved from javax.annotation when the Jakarta EE namespace migration happened; Spring 6 dropped javax support).
- Why might @PreDestroy never run in a standalone application?Destruction only happens when the context is closed. If you never call context.close() or registerShutdownHook(), the JVM can exit without the container invoking destroy callbacks.
saying these in an interview costs you the question
- Claiming @PostConstruct runs inside/before the constructor (it runs after construction and injection)
- Thinking prototype beans get their destroy callbacks invoked by Spring
- Saying @PreDestroy always fires even if the context is never closed