Here I systematically explore Spring's lifecycle methods by implementing them one by one in a structured way π
Step 1: Understanding registerBeanDefinition() in Practice
We are starting with how Spring registers bean definitions before instantiation using BeanDefinitionRegistry.
πΉ What Happens at This Stage?
- Spring loads and registers bean definitions from:
@ComponentScan-
@Beanmethods inside@Configuration - XML-based configuration (if used)
- At this point, no beans are instantiated yet! Spring is just storing the metadata about each bean.
πΉ Practical Example: Registering a Bean Manually
- We define a class
CustomBeanRegistrarthat implementsBeanDefinitionRegistryPostProcessor. - Inside
postProcessBeanDefinitionRegistry(), we register a bean definition manually. - We create a bean class (
MyDynamicBean) that will be instantiated when accessed.
@Configuration
public class CustomBeanRegistrar implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// Define a new bean manually
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MyDynamicBean.class);
registry.registerBeanDefinition("myDynamicBean", beanDefinition);
System.out.println("Custom bean definition registered: MyDynamicBean");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// Not needed for this example
}
}
πΉ Key Takeaways
β
This method is used to dynamically register beans (e.g., based on conditions, external configs).
β
At this stage, beans are just "blueprints"βactual instances are not created yet.
β
Best Practice: Only use this when dynamic bean registration is needed (e.g., custom plugins).
Step 2: Understanding postProcessBeforeInstantiation()
Now that we've covered bean definition registration, the next lifecycle method is InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation().
πΉ What Happens at This Stage?
- This method runs before the actual instantiation of a bean.
- It allows you to:
- Modify or replace the bean instance before it is created.
- Prevent normal instantiation by returning a proxy or a custom instance.
πΉ Practical Example: Intercepting Bean Instantiation
We've implemented InstantiationAwareBeanPostProcessor to log when this method is called.
@Component
class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("Before instantiating bean: " + beanName);
return null; // Returning null allows normal instantiation
}
}
π‘ Key Observations
β
This method executes before the constructor is called.
β
You can return a different instance instead of allowing the default instantiation.
β
Best Practice: Use it for AOP (Aspect-Oriented Programming) scenarios like creating proxies.
Step 3: Understanding setBeanName()
Now that we've covered postProcessBeforeInstantiation(), the next lifecycle method is BeanNameAware#setBeanName().
πΉ What Happens at This Stage?
- After the bean is instantiated, Spring calls
setBeanName(). - This allows the bean to know its own name in the application context.
- This happens before dependency injection.
πΉ Practical Example: Bean Recognizing Its Own Name
We've extended the existing application to include BeanNameAware inside MyDynamicBean:
class MyDynamicBean implements BeanNameAware {
private String beanName;
public MyDynamicBean() {
System.out.println("MyDynamicBean instantiated!");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name set: " + name);
}
}
π‘ Key Observations
β
The method is called immediately after instantiation.
β
The bean can store its own name for later use.
β
Best Practice: Use this when the bean needs to log or manipulate its own name.
Step 4: Understanding setBeanClassLoader()
Now that we've covered setBeanName(), the next lifecycle method is BeanClassLoaderAware#setBeanClassLoader().
πΉ What Happens at This Stage?
- After setting the bean name, Spring calls
setBeanClassLoader(ClassLoader classLoader). - This method allows the bean to access the ClassLoader that loaded it.
- The ClassLoader is responsible for loading classes into memory.
πΉ Practical Example: Accessing Bean ClassLoader
We've extended our MyDynamicBean class to implement BeanClassLoaderAware:
class MyDynamicBean implements BeanNameAware, BeanClassLoaderAware {
private String beanName;
private ClassLoader classLoader;
public MyDynamicBean() {
System.out.println("MyDynamicBean instantiated!");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name set: " + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("Bean ClassLoader set: " + classLoader);
}
}
π‘ Key Observations
β
The setBeanClassLoader() method is called after setBeanName().
β
The ClassLoader allows the bean to dynamically load classes at runtime.
β
Best Practice: Use it when you need to load classes dynamically (e.g., plugins, external dependencies).
Step 5: Understanding setBeanFactory()
Now that we've covered setBeanClassLoader(), the next lifecycle method is BeanFactoryAware#setBeanFactory().
πΉ What Happens at This Stage?
- After setting the bean name and class loader, Spring calls
setBeanFactory(). - This method provides access to the
BeanFactory, which is the core Spring container that holds all bean instances. - The bean can now programmatically retrieve other beans from the factory.
πΉ Practical Example: Accessing the BeanFactory
We've extended MyDynamicBean to implement BeanFactoryAware:
class MyDynamicBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware {
private String beanName;
private ClassLoader classLoader;
private BeanFactory beanFactory;
public MyDynamicBean() {
System.out.println("MyDynamicBean instantiated!");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name set: " + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("Bean ClassLoader set: " + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("BeanFactory set: " + beanFactory);
}
}
π‘ Key Observations
β
The setBeanFactory() method is called after setBeanClassLoader().
β
The BeanFactory allows beans to retrieve other beans programmatically.
β
Best Practice: Use dependency injection (@Autowired) instead of directly accessing BeanFactory, unless you need dynamic lookups.
Step 6: Understanding setEnvironment()
Now that we've covered setBeanFactory(), the next lifecycle method is EnvironmentAware#setEnvironment().
πΉ What Happens at This Stage?
- After setting bean factory access, Spring calls
setEnvironment(). - This method allows the bean to access environment properties, such as:
- System properties (
System.getProperty("key")) - Application properties (
application.properties/application.yml) - Profiles (
spring.profiles.active) - Other configurable settings.
- System properties (
πΉ Practical Example: Accessing Environment Variables
We've extended MyDynamicBean to implement EnvironmentAware:
class MyDynamicBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware {
private String beanName;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private Environment environment;
public MyDynamicBean() {
System.out.println("MyDynamicBean instantiated!");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name set: " + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("Bean ClassLoader set: " + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("BeanFactory set: " + beanFactory);
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
System.out.println("Environment set: " + environment);
System.out.println("Active Profile: " + environment.getActiveProfiles());
System.out.println("Server Port: " + environment.getProperty("server.port"));
}
}
π‘ Key Observations
β
The setEnvironment() method is called after setBeanFactory().
β
The environment allows access to application properties and profiles.
β
Best Practice: Use it when a bean needs to read environment properties dynamically.
Step 7: Understanding setEmbeddedValueResolver()
Now that we've covered setEnvironment(), the next lifecycle method is EmbeddedValueResolverAware#setEmbeddedValueResolver().
πΉ What Happens at This Stage?
- After setting environment access, Spring calls
setEmbeddedValueResolver(). - This method provides a StringValueResolver that allows resolving embedded placeholders (e.g.,
${server.port}or#{expression}). - Useful when dealing with property placeholders or Spring Expression Language (SpEL).
πΉ Practical Example: Resolving Embedded Placeholders
We've extended MyDynamicBean to implement EmbeddedValueResolverAware:
class MyDynamicBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware, EmbeddedValueResolverAware {
private String beanName;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private Environment environment;
private StringValueResolver valueResolver;
public MyDynamicBean() {
System.out.println("MyDynamicBean instantiated!");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name set: " + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("Bean ClassLoader set: " + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("BeanFactory set: " + beanFactory);
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
System.out.println("Environment set: " + environment);
System.out.println("Active Profile: " + environment.getActiveProfiles());
System.out.println("Server Port: " + environment.getProperty("server.port"));
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
String resolvedValue = valueResolver.resolveStringValue("${server.port}");
System.out.println("Embedded Value Resolved: " + resolvedValue);
}
}
π‘ Key Observations
β
The setEmbeddedValueResolver() method is called after setEnvironment().
β
Allows resolving placeholders like ${property.name} dynamically.
β
Best Practice: Use this when a bean needs to dynamically resolve properties or SpEL expressions.
Step 8: Understanding setResourceLoader()
Now that we've covered setEmbeddedValueResolver(), the next lifecycle method is ResourceLoaderAware#setResourceLoader().
πΉ What Happens at This Stage?
- After setting the embedded value resolver, Spring calls
setResourceLoader(). - This method provides a ResourceLoader, which allows loading external resources such as:
-
Files from the classpath (
classpath:/config.xml) -
Files from the file system (
file:/tmp/config.xml) -
URLs (
https://example.com/config.xml)
-
Files from the classpath (
πΉ Practical Example: Accessing Resources
We've extended MyDynamicBean to implement ResourceLoaderAware:
class MyDynamicBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware {
private String beanName;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private Environment environment;
private StringValueResolver valueResolver;
private ResourceLoader resourceLoader;
public MyDynamicBean() {
System.out.println("MyDynamicBean instantiated!");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name set: " + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("Bean ClassLoader set: " + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("BeanFactory set: " + beanFactory);
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
System.out.println("Environment set: " + environment);
System.out.println("Active Profile: " + environment.getActiveProfiles());
System.out.println("Server Port: " + environment.getProperty("server.port"));
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
String resolvedValue = valueResolver.resolveStringValue("${server.port}");
System.out.println("Embedded Value Resolved: " + resolvedValue);
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
System.out.println("ResourceLoader set: " + resourceLoader);
// Example usage: Load a file from classpath
System.out.println("Loading resource example: " + resourceLoader.getResource("classpath:application.properties").exists());
}
}
π‘ Key Observations
β
The setResourceLoader() method is called after setEmbeddedValueResolver().
β
The ResourceLoader allows loading external files dynamically.
β
Best Practice: Use ResourceLoader when a bean needs to load external configurations or static assets.
Step 9: Understanding setApplicationEventPublisher()
Now that we've covered setResourceLoader(), the next lifecycle method is ApplicationEventPublisherAware#setApplicationEventPublisher().
πΉ What Happens at This Stage?
- After setting the resource loader, Spring calls
setApplicationEventPublisher(). - This method provides an ApplicationEventPublisher, which allows the bean to:
- Publish custom application events.
-
Trigger event-driven components (such as
@EventListener).
πΉ Practical Example: Publishing an Event
We've extended MyDynamicBean to implement ApplicationEventPublisherAware:
class MyDynamicBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware {
private String beanName;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private Environment environment;
private StringValueResolver valueResolver;
private ResourceLoader resourceLoader;
private ApplicationEventPublisher eventPublisher;
public MyDynamicBean() {
System.out.println("MyDynamicBean instantiated!");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean name set: " + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("Bean ClassLoader set: " + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("BeanFactory set: " + beanFactory);
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
System.out.println("Environment set: " + environment);
System.out.println("Active Profile: " + environment.getActiveProfiles());
System.out.println("Server Port: " + environment.getProperty("server.port"));
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
String resolvedValue = valueResolver.resolveStringValue("${server.port}");
System.out.println("Embedded Value Resolved: " + resolvedValue);
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
System.out.println("ResourceLoader set: " + resourceLoader);
System.out.println("Loading resource example: " + resourceLoader.getResource("classpath:application.properties").exists());
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
System.out.println("ApplicationEventPublisher set: " + eventPublisher);
// Publish a custom event
eventPublisher.publishEvent(new CustomEvent(this, "Test Event Message"));
}
}
// Custom Event Class
class CustomEvent extends org.springframework.context.ApplicationEvent {
private final String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
// Listener for Custom Events
@Component
class CustomEventListener {
@org.springframework.context.event.EventListener
public void handleCustomEvent(CustomEvent event) {
System.out.println("Received Custom Event: " + event.getMessage());
}
}
π‘ Key Observations
β
The setApplicationEventPublisher() method is called after setResourceLoader().
β
The ApplicationEventPublisher allows beans to trigger application events.
β
Best Practice: Use ApplicationEventPublisher for decoupling components via event-driven programming.
Step 10: Understanding setMessageSource()
Now that we've covered setApplicationEventPublisher(), the next lifecycle method is MessageSourceAware#setMessageSource().
πΉ What Happens at This Stage?
- After setting the ApplicationEventPublisher, Spring calls
setMessageSource(). - This method provides a MessageSource, which allows beans to:
- Retrieve localized messages (for internationalization).
-
Access
messages.propertiesfiles for text translations.
πΉ Practical Example: Retrieving Messages
We've extended MyDynamicBean to implement MessageSourceAware:
@Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
System.out.println("MessageSource set: " + messageSource);
// Retrieve a message from messages.properties (if configured)
System.out.println("Example message: " + messageSource.getMessage("example.message", null, "Default Message", null));
}
π‘ Key Observations
β
The setMessageSource() method is called after setApplicationEventPublisher().
β
MessageSource allows retrieving localized messages dynamically.
β
Best Practice: Use MessageSource for internationalization and text localization.
Step 11: Understanding setApplicationContext()
Now that we've covered setMessageSource(), the next lifecycle method is ApplicationContextAware#setApplicationContext().
πΉ What Happens at This Stage?
- After setting the MessageSource, Spring calls
setApplicationContext(). - This method provides an ApplicationContext, which allows beans to:
- Access all beans in the Spring container.
- Retrieve environment properties.
- Interact with the lifecycle of the entire application.
πΉ Practical Example: Accessing the ApplicationContext
We've extended MyDynamicBean to implement ApplicationContextAware:
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
System.out.println("ApplicationContext set: " + applicationContext);
// Example: Retrieving a bean dynamically
MyDynamicBean anotherBean = applicationContext.getBean(MyDynamicBean.class);
System.out.println("Retrieved bean from ApplicationContext: " + anotherBean);
}
π‘ Key Observations
β
The setApplicationContext() method is called after setMessageSource().
β
ApplicationContext provides access to all registered beans dynamically.
β
Best Practice: Use @Autowired instead of manually accessing the ApplicationContext, unless necessary.
Step 12: Understanding setServletContext()
Now that we've covered setApplicationContext(), the next lifecycle method is ServletContextAware#setServletContext() (applicable for web applications).
πΉ What Happens at This Stage?
- After setting the ApplicationContext, Spring calls
setServletContext(). - This method provides access to the ServletContext, allowing the bean to:
- Interact with web-related resources.
- Retrieve application-wide configuration parameters.
- Access web-related objects like request/session attributes.
πΉ Practical Example: Accessing the ServletContext
We've extended MyDynamicBean to implement ServletContextAware:
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
System.out.println("ServletContext set: " + servletContext);
// Example: Retrieving a context parameter
String contextParam = servletContext.getInitParameter("someConfig");
System.out.println("Context Parameter: " + contextParam);
}
π‘ Key Observations
β
The setServletContext() method is called after setApplicationContext().
β
ServletContext allows interaction with web-specific components.
β
Best Practice: Use ServletContext only when necessary; prefer Spring beans for managing web configurations.
Step 13: Understanding postProcessBeforeInitialization()
Now that we've covered setServletContext(), the next lifecycle method is BeanPostProcessor#postProcessBeforeInitialization().
πΉ What Happens at This Stage?
- Spring calls
postProcessBeforeInitialization()before the initialization callbacks (@PostConstruct,InitializingBean#afterPropertiesSet()). - This method allows beans to:
- Modify bean properties before initialization.
- Apply proxying, validation, or logging before initialization.
πΉ Practical Example: Intercepting Bean Initialization
We've added a BeanPostProcessor to modify beans before initialization:
@Component
class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization called for: " + beanName);
return bean;
}
}
π‘ Key Observations
β
The postProcessBeforeInitialization() method is called before @PostConstruct and afterPropertiesSet().
β
Best Practice: Use this for bean modifications, logging, or applying custom initialization logic.
Step 14: Understanding @PostConstruct and afterPropertiesSet()
Now that we've covered postProcessBeforeInitialization(), the next lifecycle methods are @PostConstruct and InitializingBean#afterPropertiesSet().
πΉ What Happens at This Stage?
- Spring calls initialization callbacks after
postProcessBeforeInitialization(), including:-
@PostConstruct: A method annotated with@PostConstructruns after dependency injection is completed. -
afterPropertiesSet(): This method is part of theInitializingBeaninterface and runs after all properties are set.
-
- These methods are typically used for:
- Validating required properties.
- Setting up resources after dependency injection.
πΉ Practical Example: Using @PostConstruct and afterPropertiesSet()
We've modified MyDynamicBean to implement InitializingBean and added a @PostConstruct method:
@PostConstruct
public void postConstruct() {
System.out.println("@PostConstruct method executed");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet() method executed");
}
π‘ Key Observations
β
@PostConstruct runs before afterPropertiesSet().
β
Best Practice: Use @PostConstruct for setup logic, and afterPropertiesSet() when implementing InitializingBean.
β
Avoid implementing InitializingBean unless absolutely necessary, as @PostConstruct is more flexible.
Step 15: Understanding postProcessAfterInitialization()
Now that we've covered @PostConstruct and afterPropertiesSet(), the next lifecycle method is BeanPostProcessor#postProcessAfterInitialization().
πΉ What Happens at This Stage?
- Spring calls
postProcessAfterInitialization()after initialization callbacks (@PostConstruct,afterPropertiesSet()). - This method allows:
- Applying proxying mechanisms (e.g., AOP).
- Performing post-initialization modifications (e.g., wrapping the bean).
πΉ Practical Example: Intercepting Bean Post-Initialization
We've extended CustomBeanPostProcessor to implement postProcessAfterInitialization():
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization called for: " + beanName);
return bean;
}
π‘ Key Observations
β
The postProcessAfterInitialization() method is called after @PostConstruct and afterPropertiesSet().
β
Best Practice: Use this method for applying proxies or final modifications after the bean is fully initialized.
Step 16: Understanding @PreDestroy and destroy()
Now that we've covered postProcessAfterInitialization(), the final lifecycle methods are @PreDestroy and DisposableBean#destroy().
πΉ What Happens at This Stage?
- Spring calls destruction callbacks before shutting down the application.
- These methods ensure proper resource cleanup:
-
@PreDestroy: Runs before the bean is destroyed, ideal for closing resources. -
destroy(): FromDisposableBean, allows executing destruction logic.
-
πΉ Practical Example: Cleaning Up Resources
We've modified MyDynamicBean to implement DisposableBean and added a @PreDestroy method:
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy method executed");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy() method executed");
}
π‘ Key Observations
β
@PreDestroy runs before destroy().
β
Best Practice: Use @PreDestroy for graceful shutdown and destroy() if implementing DisposableBean.
β
Avoid implementing DisposableBean unless required, as @PreDestroy is more flexible.
Happy coding π
Top comments (0)