Understanding ApplicationContextAware and @PostConstruct in Spring Boot

A cup of JAVA coffee with NeeSri
7 min readJan 31, 2025

Introduction

Spring Boot is known for its powerful dependency injection mechanism, which manages beans efficiently. However, there are scenarios where you need to:

✅ Access Spring beans in static methods or spring non-managed classes.
✅ Initialize a bean with dependencies after all Spring beans are loaded.

To address these needs, Spring provides:

  • ApplicationContextAware: Allows manual access to the ApplicationContext, enabling dynamic retrieval of beans.
  • @PostConstruct: Ensures a method runs after the bean has been initialized.

In this blog, we’ll explore both concepts with a real-world use case.

What is ApplicationContextAware?

ApplicationContextAware is an interface in Spring that allows a bean to access the ApplicationContext.

How It Works:

  1. When a class implements ApplicationContextAware, Spring injects the ApplicationContext into it.
  2. This allows the class to dynamically fetch other beans at runtime.

Example Implementation:

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class BeanUtil implements ApplicationContextAware {

private static ApplicationContext context;

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}

public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}

public static <T> T getBean(String name, Class<T> beanClass) {
return context.getBean(name, beanClass);
}
}

Explanation:

  1. @Component Annotation:
  • This makes BeanUtil a Spring-managed bean so that Spring can inject the ApplicationContext into it.

2. Implements ApplicationContextAware:

  • This allows BeanUtil to capture the ApplicationContext when the application starts.

3. Stores the ApplicationContext in a Static Variable:

  • Spring injects the ApplicationContext into setApplicationContext().
  • Since the context is stored in a static variable, it can be accessed anywhere in the application.

4. Provides getBean() Methods:

  • getBean(Class<T> beanClass): Fetches a bean by its class type.
  • getBean(String name, Class<T> beanClass): Fetches a bean by its name and class type.

Use Case: Fetching a Bean in a Static Method

public class UtilityClass {

public static void performTask() {
EmailService emailService = BeanUtil.getBean(EmailService.class);
emailService.sendEmail("user@example.com", "Welcome!", "Hello, welcome to our service!");
}
}

Why Use This?

  • UtilityClass is not managed by Spring, so we cannot use @Autowired.
  • Using BeanUtil.getBean() allows us to retrieve EmailService dynamically.

What is @PostConstruct?

@PostConstruct is an annotation used in Spring to execute a method after the bean has been created and dependencies injected.

Why is this useful?

  • Ensures initialization logic runs only after all dependencies are available.
  • Helps in setting up caches, loading configurations, or running post-initialization tasks.

Example Implementation

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Service;

@Service
public class CacheService {

@PostConstruct
public void initializeCache() {
System.out.println("Loading cache...");
// Load data from the database into cache
}
}

When does initializeCache() run?

  • After CacheService is created and dependencies are injected.
  • Ensures cache loading happens at startup.

Use Case: Combining ApplicationContextAware and @PostConstruct

Scenario: Initializing a Cache with Data from a Database

  • We need to load user data into a cache when the application starts.
  • We want to refresh this cache manually from a static method.

Step 1: Create UserService to Fetch Data

import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserService {

public List<String> getAllUsers() {
return List.of("Alice", "Bob", "Charlie");
}
}

Step 2: Implement CacheManager

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;

@Component
public class CacheManager {

private UserService userService;

public CacheManager(UserService userService) {
this.userService = userService;
}

@PostConstruct
public void initializeCache() {
System.out.println("Initializing Cache...");
loadUserCache();
}

public void loadUserCache() {
List<String> users = userService.getAllUsers();
System.out.println("Users cached: " + users);
}
}

What happens here?

  • initializeCache() runs after Spring creates the CacheManager bean.
  • Calls loadUserCache() to fetch and store user data.

Step 3: Implement BeanUtil to Access Beans Dynamically

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class BeanUtil implements ApplicationContextAware {

private static ApplicationContext context;

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}

public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}

Why is this needed?

  • BeanUtil allows fetching CacheManager in non-Spring-managed classes.

Step 4: Manually Refresh Cache from a Static Method

public class StaticCacheLoader {

public static void refreshCache() {
CacheManager cacheManager = BeanUtil.getBean(CacheManager.class);
System.out.println("Refreshing cache manually...");
cacheManager.loadUserCache();
}
}

Why Use BeanUtil.getBean()?

  • StaticCacheLoader is not a Spring bean, so @Autowired won’t work.
  • BeanUtil lets us access CacheManager dynamically.

Step 5: Running the Application

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);

// Call static method to refresh cache after startup
StaticCacheLoader.refreshCache();
}
}

Expected Output:

Initializing Cache...
Users cached: [Alice, Bob, Charlie]
Refreshing cache manually...
Users cached: [Alice, Bob, Charlie]

What Happened?

  1. @PostConstruct automatically loaded the cache at startup.
  2. The static method refreshCache() was called to reload the cache manually.

Conclusion

Spring Boot provides ApplicationContextAware and @PostConstruct to handle dynamic bean access and post-initialization tasks. Combining these two features allows for flexible and scalable application design.

This pattern is especially useful for:
Cache Management
Configuration Preloading
Static Utility Classes

🔥 Interview Questions & Answers

1. What happens if a @PostConstruct method throws an exception?

Answer:

  • If a @PostConstruct method throws an exception, the bean fails to initialize.
  • The Spring application context will not start, and an exception will be thrown.
  • This is because @PostConstruct is part of the bean's lifecycle, and if it fails, the bean is considered invalid.
@Component
public class MyBean {
@PostConstruct
public void init() {
throw new RuntimeException("Initialization failed!");
}
}

This will cause the application context to fail with a BeanCreationException

2. Can a @PostConstruct method be private or final?

Answer:

  • No, a @PostConstruct method cannot be private or final.
  • Spring uses reflection to invoke the @PostConstruct method, and private or final methods cannot be accessed or overridden via reflection.
  • If you mark the method as private or final, Spring will throw an exception.
@Component
public class MyBean {
@PostConstruct
private void init() { // This will cause an error
System.out.println("Initializing...");
}
}

3. Can a @PostConstruct method be static?

Answer:

  • No, a @PostConstruct method cannot be static.
  • Static methods are not tied to an instance of the class, and @PostConstruct is meant to initialize a specific bean instance.
  • If you mark the method as static, Spring will ignore it.
@Component
public class MyBean {
@PostConstruct
public static void init() { // This will be ignored
System.out.println("Initializing...");
}
}

4. What is the order of execution if multiple lifecycle methods are present (e.g., @PostConstruct, InitializingBean, and init-method)?

Answer:
The order of execution is:

  1. Constructor
  2. Dependency injection (e.g., @ Autowired )
  3. @PostConstruct method
  4. InitializingBean.afterPropertiesSet() (if implemented)
  5. Custom init-method (if specified in XML or @Bean)
@Component
public class MyBean implements InitializingBean {
public MyBean() {
System.out.println("Constructor");
}

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

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

public void initMethod() {
System.out.println("Custom init-method");
}
}
------------OUTPUT---------------
Constructor
@PostConstruct
InitializingBean.afterPropertiesSet
Custom init-method

5. Can a @PostConstruct method be overridden in a subclass?

Answer:

  • Yes, a @PostConstruct method can be overridden in a subclass.
  • However, only the overridden method in the subclass will be called, not the one in the parent class.
  • If you want both the parent and child methods to execute, you need to explicitly call the parent method from the child method.
@Component
public class Parent {
@PostConstruct
public void init() {
System.out.println("Parent @PostConstruct");
}
}

@Component
public class Child extends Parent {
@Override
@PostConstruct
public void init() {
super.init(); // Call parent's method
System.out.println("Child @PostConstruct");
}
}
--------------output---------
Parent @PostConstruct
Child @PostConstruct

6. What happens if you define multiple @PostConstruct methods in a single class?

Answer:

  • Only one method in a class can be annotated with @ PostConstruct.
  • If you define multiple methods with @PostConstruct, Spring will throw an exception during startup.
@Component
public class MyBean {
@PostConstruct
public void init1() {
System.out.println("Init 1");
}

@PostConstruct
public void init2() { // This will cause an error
System.out.println("Init 2");
}
}

7. Can @PostConstruct be used in a non-Spring-managed class?

Answer:

  • No, @PostConstruct only works in Spring-managed beans (e.g., classes annotated with @Component, @Service, @Repository, etc.).
  • If you try to use it in a non-Spring-managed class, it will have no effect.

8. What is the difference between @PostConstruct and a constructor?

Answer:

  • Constructor: Called when the bean is instantiated. Dependencies are not yet injected.
  • @PostConstruct: Called after the bean is instantiated and all dependencies are injected. It is used for initialization logic that depends on injected dependencies.
@Component
public class MyBean {
private final MyRepository myRepository;

public MyBean(MyRepository myRepository) {
this.myRepository = myRepository;
// Constructor: Dependencies are injected
}

@PostConstruct
public void init() {
myRepository.doSomething();
// @PostConstruct: Safe to use dependencies
}
}

9. Can @PostConstruct be used in a prototype-scoped bean?

Answer:

  • Yes, @PostConstruct can be used in prototype-scoped beans.
  • However, it will be called every time a new instance of the bean is created, since prototype beans are not singleton.

10. What happens if a @PostConstruct method is not called?

Answer:

  • If a @PostConstruct method is not called, it could be due to:
  1. The class is not a Spring-managed bean (e.g., missing @Component).
  2. The method is private, static, or final.
  3. The method is overridden in a subclass without calling super.init().
  4. The Spring context failed to start due to other errors.

--

--

No responses yet