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). Then 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. This gives the bean access to its own name, the factory, or the full context. BeanPostProcessor.postProcessBeforeInitialization()— runs for every bean, for every registeredBeanPostProcessor. This is 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. That means 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 over implementing InitializingBean/DisposableBean directly. They're simple, annotation-based, and don't couple you to Spring-specific interfaces — unless you're writing framework-level code that specifically needs the interface-based contract.