skip to content

Init & Destroy Callbacks

Three ways to hook initialization and destruction — @PostConstruct/@PreDestroy, InitializingBean/DisposableBean, and @Bean initMethod/destroyMethod — and the order Spring invokes them in. Expect to be asked which you would pick and why the annotations beat the interfaces.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What are the ways to run custom code when a Spring bean is initialized or destroyed?

level: juniorimportance: must knowfreq 70%

answer

  1. Three mechanisms: annotation, interface, @Bean method
  2. @PostConstruct after injection, @PreDestroy on shutdown
  3. InitializingBean.afterPropertiesSet / DisposableBean.destroy
  4. jakarta.annotation package (Spring 6)
  5. prototypes: no destroy callback

basics

~10 s

Three 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 s

Spring 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 lines
java
import 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

for a junior

Know the three mechanisms exist and what @PostConstruct/@PreDestroy do.

for a middle

Know the exact packages, that destroy fires only on context close, and that @Bean(initMethod/destroyMethod) is for third-party classes.

for a senior

Explain inferred destroyMethod, prototype non-destruction, and why annotations are preferred over interfaces.

for a principal

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

context

open as a page

If a bean uses @PostConstruct, implements InitializingBean, and also declares an @Bean initMethod, in what order do the init callbacks run?

level: middleimportance: must knowfreq 65%

basics

~10 s

First @PostConstruct, then InitializingBean.afterPropertiesSet(), then the @Bean initMethod. Destruction mirrors it: @PreDestroy, then DisposableBean.destroy(), then the @Bean destroyMethod.

open as a page

Explain @Bean's 'inferred' destroyMethod and why an AutoCloseable third-party bean sometimes gets close() called without you configuring anything.

level: seniorimportance: should knowfreq 40%

basics

~10 s

@Bean's destroyMethod defaults to the sentinel "(inferred)": if the bean has a public close() or shutdown() method (or implements AutoCloseable), Spring calls it automatically on shutdown. Set destroyMethod="" to turn this off.

open as a page

Why is a @PreDestroy method on a prototype-scoped bean never called, and what can you do about it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Spring doesn't manage a prototype's full lifecycle — it creates and hands off the instance without tracking it, so no destroy callback runs. To clean up, do it manually or use a custom BeanPostProcessor/DisposableBeanAdapter yourself.

open as a page

Why place resource-opening or dependency-validation logic in @PostConstruct rather than the constructor, and how does this interact with AOP proxies and post-processors?

level: principalimportance: should knowfreq 35%

basics

~10 s

In the constructor, field/setter-injected dependencies aren't set yet, and the bean isn't fully post-processed. @PostConstruct runs after injection, so collaborators are available. But it runs before AOP proxying, so self-invocation there isn't advised.

open as a page