Describe the Spring bean lifecycle and the callback hooks available (@PostConstruct, @PreDestroy, InitializingBean, etc.).
Quick Answer
A singleton bean's lifecycle runs: instantiation, dependency injection (constructor/setter/field), then any Aware interface callbacks (BeanNameAware, ApplicationContextAware), then BeanPostProcessor 'before initialization', then initialization callbacks (@PostConstruct, then InitializingBean.afterPropertiesSet(), then a custom init-method), then BeanPostProcessor 'after initialization' (where proxies like AOP advice are typically applied), and the bean is ready for use — until container shutdown triggers destruction callbacks (@PreDestroy, DisposableBean.destroy(), or a custom destroy-method).
Detailed Answer
For a singleton bean, the full lifecycle (simplified) runs in this order:
- Instantiation — Spring calls the bean's constructor (resolving constructor-injected dependencies at this point).
- Dependency injection — setter and field injection (
@Autowiredon setters/fields) happens after construction. - Aware interface callbacks — if the bean implements marker interfaces like
BeanNameAware,BeanFactoryAware, orApplicationContextAware, Spring calls their respective setter methods, giving the bean access to its own name, the factory, or the full context. BeanPostProcessor.postProcessBeforeInitialization()— runs for every bean, for every registeredBeanPostProcessor(a Spring extension point most application code never implements directly, but many Spring features are built on it internally).- Initialization callbacks, in this order:
@PostConstruct-annotated method (JSR-250, the most common modern choice).InitializingBean.afterPropertiesSet()(if the bean implements that interface — an older, interface-based alternative).- A custom
init-methodspecified via@Bean(initMethod = "...")or XML.
BeanPostProcessor.postProcessAfterInitialization()— this is where Spring applies AOP proxying: if the bean needs a proxy (for@Transactional,@Async, custom aspects, etc.), the proxy is created and returned here, meaning the object other beans actually get injected may be a proxy wrapping the real instance.- Bean is ready — used by the application for its normal lifetime.
- Destruction (on container shutdown), in a similar layered order:
@PreDestroy, thenDisposableBean.destroy(), then a customdestroy-method.
@Component
class CacheWarmer implements InitializingBean, DisposableBean {
@PostConstruct
void warmUp() { /* runs first among init callbacks */ }
@Override
public void afterPropertiesSet() { /* runs after @PostConstruct */ }
@PreDestroy
void logShutdown() { /* runs before destroy() */ }
@Override
public void destroy() { /* release resources */ }
}
Practical guidance: prefer @PostConstruct/@PreDestroy (simple, annotation-based, no coupling to Spring-specific interfaces) over implementing InitializingBean/DisposableBean directly, unless you're writing framework-level code that specifically needs the interface-based contract.