Spring BootBean Lifecycleintermediate
Updated:

Spring Boot Bean Lifecycle Interview Questions and Answers

7 min read

The bean lifecycle questions Spring interviewers actually ask — the full init and destroy callback order, @PostConstruct, BeanPostProcessor and the prototype trap.

TL;DR – Quick Answer

Spring Boot bean lifecycle interviews test whether you know the exact order the container runs: instantiation, dependency injection, Aware callbacks, BeanPostProcessor before-init, @PostConstruct, InitializingBean.afterPropertiesSet, custom init-method, BeanPostProcessor after-init, then use, and finally @PreDestroy and DisposableBean.destroy on shutdown. The high-value follow-ups are why destroy callbacks never run for prototype beans and what BeanPostProcessor is used for.

On This Page

The bean lifecycle is where a Spring Boot interview separates people who wired up beans from people who understand the container that manages them. The questions are precise: name the callbacks, order them, and explain who invokes them and when. This set covers the questions asked across intermediate rounds — the full init-and-destroy sequence, the annotation versus interface choices, BeanPostProcessor, and the prototype trap — each with a spoken answer and the follow-up interviewers reach for.

Why interviewers ask about the bean lifecycle

Every Spring Boot application is a graph of beans that the container creates, wires, initializes and eventually destroys. Understanding that sequence is what lets you place initialization logic correctly, release resources cleanly, and reason about why a @Transactional proxy or an @Autowired field works at all. Interviewers use lifecycle questions because the answer is verifiable to the exact step — you either know the order or you don't.

Q1. Walk me through the full Spring bean lifecycle.

The container instantiates the bean, populates its properties and injects dependencies, invokes any Aware callbacks, runs BeanPostProcessor.postProcessBeforeInitialization, then the initialization callbacks in order — @PostConstruct, InitializingBean.afterPropertiesSet(), the custom init-method — then BeanPostProcessor.postProcessAfterInitialization. The bean is now ready for use. On context shutdown it runs @PreDestroy, DisposableBean.destroy(), then the custom destroy-method.

The mental model is two phases with a "ready" state between them. Creation covers instantiation through the after-initialization post-processor; destruction covers the shutdown callbacks. The three initialization hooks all fire after injection, which is precisely why @PostConstruct is the safe place to validate that required dependencies arrived.

@Component
class ReportService implements InitializingBean, DisposableBean {

    @PostConstruct
    void postConstruct() { System.out.println("1. @PostConstruct"); }

    @Override
    public void afterPropertiesSet() { System.out.println("2. afterPropertiesSet"); }

    @Override
    public void destroy() { System.out.println("3. DisposableBean.destroy"); }
}

Interview note: Follow-up: "if a bean has @PostConstruct, afterPropertiesSet() and an initMethod, in what order do they run?" That exact order — annotation, interface, then custom method. Reciting it confidently is the whole point of the question.

Q2. What do @PostConstruct and @PreDestroy do, and which package are they in?

@PostConstruct marks a method to run once, after the bean is fully constructed and injected; @PreDestroy marks a method to run once, just before the bean is destroyed. In Spring Boot 3 / Spring 6 they live in jakarta.annotation, not javax.annotation — the Jakarta EE namespace migration moved them.

Because they are plain annotations, the class stays free of Spring-specific interfaces, which is why they are the modern default for open/close-style lifecycle logic — opening a connection pool, starting a scheduler, warming a cache on the way up, and releasing those resources on the way down.

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

@Component
class CacheWarmer {
    @PostConstruct
    void warm() { /* preload cache after dependencies are ready */ }

    @PreDestroy
    void flush() { /* release resources before shutdown */ }
}

Interview note: Trap: "why did old @PostConstruct code stop compiling after a Spring Boot 3 upgrade?" The import moved from javax.annotation to jakarta.annotation. Knowing the namespace switch signals you have actually done a 2-to-3 migration.

Q3. InitializingBean and DisposableBean vs the annotations — which do you use?

InitializingBean forces afterPropertiesSet() and DisposableBean forces destroy(). They work, but they couple your class to Spring interfaces, so the preferred choices are @PostConstruct/@PreDestroy for your own classes and @Bean(initMethod, destroyMethod) for third-party classes you cannot annotate.

The interface approach is marginally faster because there is no reflection to find the annotated method, but that difference is irrelevant at startup and never worth the coupling. The useful thing to know is that all three mechanisms can coexist on one bean, and Spring runs them in the fixed order from Q1.

Interview note: Follow-up: "when would you actually reach for @Bean(initMethod=...)?" When the class comes from a library and you cannot add annotations to its source — you declare the init and destroy method names on the @Bean definition instead.

Q4. How do you configure init and destroy methods on a @Bean?

On a @Bean method you set initMethod and destroyMethod to the names of methods on the returned type. Spring calls them at the same lifecycle points as afterPropertiesSet() and destroy(), without the class implementing any Spring interface.

This is the clean way to manage the lifecycle of external types — a client, a pool, a native resource — inside a @Configuration class.

@Configuration
class PoolConfig {
    @Bean(initMethod = "start", destroyMethod = "shutdown")
    ConnectionPool connectionPool() {
        return new ConnectionPool();   // start()/shutdown() are its own methods
    }
}

Interview note: Trap: "what does destroyMethod default to for @Bean?" It infers a public no-arg close() or shutdown() method automatically. Set destroyMethod = "" to disable that inference when you do not want Spring calling close().

Q5. What is a BeanPostProcessor and how is it different from a BeanFactoryPostProcessor?

A BeanPostProcessor intercepts every bean instance between injection and readiness, with postProcessBeforeInitialization and postProcessAfterInitialization hooks — it operates on constructed objects. A BeanFactoryPostProcessor runs earlier and operates on bean definitions, before any bean is instantiated, letting you modify configuration metadata.

BeanPostProcessor is the engine behind much of Spring itself: @Autowired and @PostConstruct are handled by post-processors, and AOP and @Transactional proxies are created by wrapping the target in postProcessAfterInitialization.

@Component
class TimingPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String name) {
        // e.g. wrap bean in a proxy, or just observe every initialized bean
        return bean;
    }
}

Interview note: Follow-up: "how does @Transactional actually get applied?" A BeanPostProcessor returns a proxy in place of your bean after initialization, and the proxy opens and commits the transaction around your method. That is why self-invocation inside the same class bypasses it.

Q6. Where do Aware interface callbacks fit in the lifecycle?

Aware callbacks fire after dependency injection but before the initialization callbacks. BeanNameAware.setBeanName, BeanFactoryAware.setBeanFactory and ApplicationContextAware.setApplicationContext hand the bean pieces of container infrastructure it asked for.

They exist so a bean can learn its own name or reach the context, but they couple your code to Spring, so favour injecting what you need (for example ApplicationContext or Environment) over implementing the Aware interface. When you see them in an interview, the expected point is ordering: Aware comes after injection, before @PostConstruct.

Q7. Why don't destroy callbacks run for prototype beans?

Because Spring manages a prototype bean only until it hands the instance to the requester. It keeps no reference afterward, so it has nothing to call @PreDestroy or destroy() on. The container fully manages the creation lifecycle of a prototype but not its destruction — cleanup is the caller's responsibility.

This is one of the most reliable lifecycle traps. Singletons live in the context and get their destroy callbacks on shutdown; prototypes are created fresh on each request and then forgotten. If a prototype holds a resource that must be released, you release it yourself or wrap acquisition so the caller controls teardown.

@Component
@Scope("prototype")
class Task {
    @PreDestroy
    void cleanup() { /* NEVER called by the container for a prototype */ }
}

Interview note: Trap: "how would you get destroy logic to run for a prototype anyway?" Call it explicitly, or register a custom DisposableBeanAdapter/destruction callback — but the honest answer interviewers want first is "the container will not do it for you."

Q8. What is the lifecycle difference between a singleton and a prototype bean?

A singleton is created once, eagerly by default at context startup, cached, and destroyed on context shutdown — so it receives both init and destroy callbacks. A prototype is created lazily, once per request for the bean, receives init callbacks, and never receives destroy callbacks.

This connects the lifecycle to scope, which is why the two topics are usually asked together. The eager-versus-lazy detail matters too: singletons are instantiated up front unless marked @Lazy, so a broken singleton fails fast at startup rather than on first use.

Interview note: Follow-up: "does @Lazy change which callbacks a singleton gets?" No — it only delays creation until first access. The init callbacks still run at creation time, just later; destroy still runs on shutdown.

Q9. When exactly does @PostConstruct run relative to dependency injection?

After all dependencies are injected and after any Aware callbacks, but before the bean is exposed for use. That guarantee is why @PostConstruct is the correct place to validate configuration or derive state that depends on injected collaborators — everything it needs is already wired.

Putting that logic in the constructor is the common mistake: field and setter injection have not happened yet when the constructor runs, so injected fields are still null. Constructor injection avoids that, but even then, cross-dependency initialization belongs in @PostConstruct where the whole graph is settled.

@Service
class PricingService {
    @Autowired private RateTable rates;

    @PostConstruct
    void validate() {
        Objects.requireNonNull(rates, "rates must be injected");  // rates is ready here
    }
}

Interview note: Trap: "why is @Autowired field access null in the constructor?" The object must exist before Spring can inject into its fields, so the constructor runs first with fields unset. This is the standard argument for constructor injection.

How to prepare

Draw the lifecycle once, end to end, and annotate each arrow with who calls it — the container, a post-processor, or your code. Then build the tiny demo from Q1 with a @PostConstruct, an afterPropertiesSet() and an initMethod on one bean and run it: seeing the print order land in the exact sequence fixes it permanently, and it is the single most-asked lifecycle question. Do the same for the prototype in Q7 and watch @PreDestroy never fire.

Work through the container fundamentals on the Spring Boot learning path, then connect this page to bean scopes and dependency injection, since lifecycle, scope and injection are almost always probed as one line of questioning. A focused mock interview on the container internals is the fastest way to find where your explanation runs out and push it one callback deeper.

Frequently Asked Questions

What is the correct order of Spring bean lifecycle callbacks?
Instantiation, then property population and dependency injection, then Aware interface callbacks, then BeanPostProcessor postProcessBeforeInitialization, then @PostConstruct, then InitializingBean.afterPropertiesSet(), then the custom init-method, then BeanPostProcessor postProcessAfterInitialization. On shutdown: @PreDestroy, then DisposableBean.destroy(), then the custom destroy-method.
What is the difference between @PostConstruct and InitializingBean?
Both run initialization logic after dependencies are injected, but @PostConstruct is a jakarta.annotation marker that keeps your class free of Spring interfaces, while InitializingBean forces you to implement afterPropertiesSet() and couples the class to Spring. @PostConstruct runs first. Prefer @PostConstruct or @Bean(initMethod=...) in modern code.
Why don't destroy callbacks run for prototype beans?
Spring fully manages a prototype bean only up to the point it hands the instance to the caller. After that the container keeps no reference, so it cannot invoke @PreDestroy or destroy(). The caller owns cleanup for prototype beans, which is a frequent interview trap.
What is a BeanPostProcessor used for?
A BeanPostProcessor lets you intercept every bean between dependency injection and readiness, with hooks before and after initialization. Spring itself uses it to process annotations like @Autowired and @PostConstruct and to wrap beans in proxies for AOP and @Transactional. You implement one to customise or wrap beans container-wide.
What are Aware interfaces in Spring?
Aware interfaces such as BeanNameAware, ApplicationContextAware and EnvironmentAware let a bean receive container infrastructure during initialization. Spring calls their setter-style callbacks after injection but before initialization callbacks, so the bean can grab its name, context or environment. They couple code to Spring, so use them sparingly.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Discover CodeBegun's Java Full Stack track

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 16 July 2026 LinkedIn
Chat with us