Hook: Interviewers love this question because ApplicationContext is the heart of Spring: it creates objects, wires them together, and keeps them alive for you.
Question: What is ApplicationContext?
Answer: ApplicationContext is Spring’s main container for beans, meaning the objects that Spring creates and manages. It is where dependency injection happens, so your classes do not need to build or find their own dependencies. In Spring Boot, the framework usually creates it for you when you call SpringApplication.run(...).
Interview-Ready Answer: I think of ApplicationContext as Spring’s runtime container. It loads bean definitions, creates and wires beans, and also gives me extra services like events, resource loading, and message resolution. In Spring Boot, the context is usually started for me by SpringApplication.run(), so I focus on declaring beans and let the container manage their lifecycle.
ApplicationContext is Spring’s high-level container. A container is the object that creates, wires, configures, and destroys your application objects. Those managed objects are called beans.
It is more than a simple registry. Besides bean creation, it can resolve messages for internationalization, load resources like files or classpath entries, publish application events, and expose the current Environment such as active profiles and properties. In Spring Boot, the concrete context is usually created for you; for a web app it is commonly a servlet web server context, while a non-web app may use a simpler annotation-based context.
@SpringBootApplication, @Configuration, component scanning, auto-configuration, and property files.BeanFactoryPostProcessor hooks run first. A post-processor is a callback that can change bean recipes before any bean is created.BeanPostProcessor hooks run before and after initialization. These power features like @Autowired, @PostConstruct, transactions, and AOP proxies, where a proxy is a wrapper object that adds behavior around a real bean.Default scope matters here: Spring beans are singleton by default, which means one shared instance per context. Prototype beans are different: they are created each time you ask for them, and the container does not fully manage their destruction.
You use ApplicationContext when you want Spring to own object creation and wiring. That gives you loose coupling, easier testing, and access to cross-cutting services like events and configuration. In normal application code, you usually prefer constructor injection over calling getBean(); direct lookups are for dynamic cases where the bean choice changes at runtime.
| Aspect | ApplicationContext | BeanFactory |
|---|---|---|
| Role | Full container | Core container |
| Startup | Eager singletons | Mostly lazy |
| Extras | Events, i18n, resources | Basic DI only |
| Typical use | Most apps | Low-level cases |
The easy way to remember it: BeanFactory can build beans, but ApplicationContext can run the whole building.
Bean lookup by name or type is usually close to O(1) average time because Spring keeps internal maps of bean definitions and singleton instances. The expensive part is startup, not lookup. A small Boot service may start in a few hundred milliseconds on a warm JVM; a larger service with web, security, JPA, and many beans can take one to several seconds.
Watch these edge cases: asking for a bean by type when several candidates exist causes a NoUniqueBeanDefinitionException; asking for a missing bean causes a NoSuchBeanDefinitionException; and creating objects with new bypasses the container, so injections, transactions, and listeners will not work. Prototype beans are also a special case because they are created on demand, not eagerly at startup.
Imagine a checkout service in an e-commerce app. The context wires the payment service, injects API keys from configuration, publishes OrderPlacedEvent messages, and activates the correct implementation for the current profile, such as sandbox versus production.
Now the bug: a developer manually creates a helper with new instead of letting Spring build it. That helper depends on @Value for the payment key and on @EventListener for audit events, so both features silently stop working. In production you see failed payments, API key must not be blank errors, missing audit logs, and users retrying checkout because the page keeps returning 500.
The lesson is simple: if Spring should manage it, let the ApplicationContext create it. That is how you get injection, lifecycle hooks, proxies, and event handling consistently across the app.
package com.example.demo;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@SpringBootApplication
public class ApplicationContextDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ApplicationContextDemoApplication.class, args);
}
@Bean
CommandLineRunner demo(ApplicationContext ctx, GreetingService greetingService) {
return args -> {
System.out.println("Bean count = " + ctx.getBeanDefinitionCount());
// Lookup by type is convenient, but it fails when more than one bean matches.
try {
Formatter formatter = ctx.getBean(Formatter.class);
System.out.println(formatter.format("Asha"));
} catch (NoUniqueBeanDefinitionException ex) {
System.out.println("Type-based lookup is ambiguous: " + ex.getMessage());
}
// Name-based lookup is explicit and works when multiple implementations exist.
Formatter friendly = ctx.getBean("friendlyFormatter", Formatter.class);
System.out.println(friendly.format("Asha"));
// Missing beans fail fast, which is helpful because problems show up early.
try {
ctx.getBean("missingFormatter", Formatter.class);
} catch (NoSuchBeanDefinitionException ex) {
System.out.println("Missing bean handled: " + ex.getMessage());
}
// ApplicationContext can publish events too.
ctx.publishEvent(new OrderPlacedEvent("ORD-1001", 49.99));
// This service uses the context directly; it works, but constructor injection is usually cleaner.
System.out.println(greetingService.greet("shoutFormatter", "Asha"));
};
}
}
interface Formatter {
String format(String name);
}
@Component("friendlyFormatter")
class FriendlyFormatter implements Formatter {
@Override
public String format(String name) {
return "Hello, " + name + "!";
}
}
@Component("shoutFormatter")
class ShoutFormatter implements Formatter {
@Override
public String format(String name) {
return ("hello, " + name + "!").toUpperCase();
}
}
@Service
class GreetingService {
private final ApplicationContext ctx;
GreetingService(ApplicationContext ctx) {
this.ctx = ctx;
}
String greet(String formatterBeanName, String name) {
// Runtime lookup is useful only when the choice is dynamic.
// If the dependency is fixed, prefer constructor injection instead.
Formatter formatter = ctx.getBean(formatterBeanName, Formatter.class);
return formatter.format(name);
}
}
record OrderPlacedEvent(String orderId, double total) {
}
@Component
class OrderEventListener {
@EventListener
public void handle(OrderPlacedEvent event) {
System.out.println("Order event received: " + event.orderId() + ", total=" + event.total());
}
}
Follow-up & Tricky Questions:
ApplicationContext and BeanFactory? ApplicationContext is the richer container. BeanFactory is the lower-level core that focuses on bean creation, while ApplicationContext adds events, resources, internationalization, and eager singleton startup.SpringApplication.run() picks a concrete context, loads auto-configuration and your beans, then refreshes the container so the app is ready to use.getBean()? Use it only when the bean choice is truly dynamic, such as selecting a strategy by name or profile. For normal dependencies, constructor injection is cleaner and easier to test.ApplicationContext into a service? Yes, because it is itself available as an infrastructure object. Just be careful: doing that everywhere often hides dependencies and turns your code into service-locator style.ApplicationContext create every bean at startup? No. By default it eagerly creates singleton beans, but lazy beans and prototype beans are created later when requested.ApplicationContext the same thing as dependency injection? No. Dependency injection is the mechanism; ApplicationContext is the container that performs it and manages the whole lifecycle.new, will Spring still inject its fields? No. Only objects created and managed by the context get Spring features such as injection, AOP proxies, and lifecycle callbacks.Common Mistakes:
new for Spring-managed classes. Correction: let the context create the object so injection and proxies work.getBean() for every dependency. Correction: prefer constructor injection for fixed dependencies and reserve lookups for dynamic cases.Memory Hook: Think of ApplicationContext as the hotel front desk: it knows every room, hands out keys, wakes up services, and announces important messages.
Cheat Sheet:
ApplicationContext is Spring’s main container.SpringApplication.run().getBean() only when dynamic lookup is needed.Practice Tasks:
@Primary or by name.@EventListener to feel the context in action.