What do @PostConstruct and @PreDestroy Methods Do?
4. Prototype beans - @PreDestroy is not called -> manually or DestructionCallback
Junior Level
@PostConstruct - executed once after bean creation. @PreDestroy - executed once before bean removal.
Spring 6 / Boot 3: package changed from
javax.annotationtojakarta.annotation.
@Service
public class MyService {
@PostConstruct
public void init() {
// Called after creation and dependency injection
System.out.println("Bean created!");
}
@PreDestroy
public void cleanup() {
// Called before bean removal
System.out.println("Bean is being removed!");
}
}
When to use:
@PostConstruct-> initialization, validation@PreDestroy-> closing connections, cleanup
Middle Level
Execution Order
1. Constructor
2. @Autowired (injection)
3. @PostConstruct <- Dependencies already exist!
4. Bean is ready
On removal:
1. @PreDestroy
2. Bean removed
Why not Constructor?
// In constructor, Field/Setter dependencies are still NULL!
public MyService() {
repo.loadData(); // NPE! repo not yet injected
}
// @PostConstruct - dependencies already exist
@PostConstruct
public void init() {
repo.loadData(); // OK!
}
Singleton vs Prototype
// Singleton:
// -> @PostConstruct called once
// -> @PreDestroy called on shutdown
// Prototype:
// -> @PostConstruct called each time on creation
// -> @PreDestroy is NOT called automatically for Prototype beans. Spring does not track their destruction.
Initialization Order
1. @PostConstruct
2. InitializingBean.afterPropertiesSet()
3. @Bean(initMethod = "init")
-> Use only @PostConstruct (standard)
Senior Level
When NOT to Use @PostConstruct / @PreDestroy
- Heavy initialization - blocks context startup -> SmartLifecycle
- Calling @Transactional methods - proxy does not exist yet -> ContextRefreshedEvent
- Operations with retry - @PostConstruct does not retry -> SmartLifecycle
- Prototype beans - @PreDestroy is not called -> manually or DestructionCallback
Proxy Trap
@PostConstruct is called BEFORE proxy creation!
@Service
public class Service {
@PostConstruct
public void init() {
loadData(); // @Transactional -> does NOT work!
}
@Transactional
public void loadData() { }
}
-> Call goes to the original object, not the proxy
-> Transaction is NOT created
Solution:
@EventListener(ContextRefreshedEvent.class)
public void init() {
loadData(); // Proxy already exists -> transaction works
// ContextRefreshedEvent is published AFTER all beans are created
// And all proxies too. So calling @Transactional methods from this
// handler works correctly.
}
Inheritance
class Parent {
@PostConstruct
public void parentInit() { }
}
class Child extends Parent {
// If not overridden -> parentInit() is called
// If overridden -> childInit() is called
}
CommonAnnotationBeanPostProcessor
Responsible for processing @PostConstruct and @PreDestroy
-> Registered automatically
-> Part of the standard BPP set
Production Experience
Real scenario: @PreDestroy was not called
// Prototype bean with @PreDestroy
@Scope("prototype")
public class Connection {
@PreDestroy
public void close() { } // Will never be called!
}
// Connection leak!
// Solution: call close() manually
Best Practices
- @PostConstruct -> validation, light initialization
- @PreDestroy -> resource cleanup
- Do NOT call @Transactional methods from @PostConstruct
- Prototype -> @PreDestroy does not work
- Exception in @PostConstruct -> context will not start.
This is useful - application fails fast with incorrect configuration. But: if initialization can fail expectedly (no DB connection), use SmartLifecycle with retries instead of @PostConstruct.
- ContextRefreshedEvent -> when proxy is needed
Summary for Senior
- @PostConstruct -> after DI, BEFORE proxy
- @PreDestroy -> for Singleton only
- Proxy Trap -> @Transactional does not work in @PostConstruct
- ContextRefreshedEvent -> when proxy is ready
- Prototype -> @PreDestroy is never called
- Fail-fast -> exception in @PostConstruct = crash on startup
Interview Cheat Sheet
Must know:
@PostConstruct- executed once AFTER bean creation and dependency injection, BEFORE proxy creation@PreDestroy- executed once BEFORE bean removal, for Singleton only- In constructor, Field/Setter dependencies are still NULL, in
@PostConstruct- already injected @Transactionaldoes NOT work in@PostConstruct- proxy does not exist yet, call goes to original object- Prototype:
@PostConstructcalled each time on creation,@PreDestroyis NOT called automatically - Exception in
@PostConstruct= application will not start (fail-fast - useful for incorrect configuration) - Spring 6 / Boot 3:
jakarta.annotation.PostConstructinstead ofjavax.annotation - Order:
@PostConstruct->InitializingBean.afterPropertiesSet()->init-method(use only@PostConstruct)
Common follow-up questions:
- Why not use constructor for initialization? In constructor, Field/Setter dependencies are still NULL. For Constructor Injection - dependencies exist, but proxy does not.
- How to call
@Transactionalmethod during initialization?@EventListener(ContextRefreshedEvent.class)- all proxies already created. - What if heavy initialization in
@PostConstruct? Blocks context startup - useSmartLifecyclewith retry. - What happens to
@PreDestroyin Prototype? Never called - resource leak, clean manually.
Red flags (DO NOT say):
- “In constructor, all dependencies already exist” (for Field/Setter injection - NULL)
- ”
@PreDestroyworks for all scopes” (does NOT work for Prototype) - “You can call
@Transactionalmethods from@PostConstruct” (proxy does not exist yet, transaction is NOT created) - “Exception in
@PostConstructis bad” (actually fail-fast is useful, but for expected errors useSmartLifecycle)
Related topics:
- [[06. What is Bean Lifecycle]]
- [[07. What are the Bean Lifecycle stages]]
- [[08. What is BeanPostProcessor]]
- [[04. What is a Bean in Spring]]
- [[10. What is Bean scope]]