RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Spring Boot questions
MediumSpring Boot#156 min readJul 11, 2026

What is ApplicationContext?

spring-core
spring-boot
dependency-injection
ioc
Practice modeTest yourself instead of reading straight through

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.

🧠 Memory Map
Memory map — visual summary of this topic

What it really is

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.

How it works under the hood

  1. Spring Boot chooses a concrete context implementation and starts building it.
  2. It reads configuration from @SpringBootApplication, @Configuration, component scanning, auto-configuration, and property files.
  3. It registers bean definitions, which are recipes for creating objects, not the objects themselves.
  4. BeanFactoryPostProcessor hooks run first. A post-processor is a callback that can change bean recipes before any bean is created.
  5. Singleton beans are instantiated, constructor arguments are resolved, and dependencies are injected.
  6. 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.
  7. The context is refreshed and lifecycle events are published. At this point the app is ready to serve requests.

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.

Why you use it

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.

ApplicationContext vs BeanFactory

AspectApplicationContextBeanFactory
RoleFull containerCore container
StartupEager singletonsMostly lazy
ExtrasEvents, i18n, resourcesBasic DI only
Typical useMost appsLow-level cases

The easy way to remember it: BeanFactory can build beans, but ApplicationContext can run the whole building.

Performance and edge cases

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.

Real-world story

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.

Spring Boot
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:

  • What is the difference between 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.
  • How does Spring Boot create the context? SpringApplication.run() picks a concrete context, loads auto-configuration and your beans, then refreshes the container so the app is ready to use.
  • What is a bean post-processor? It is a callback that can modify beans before or after initialization. Spring uses these hooks for injection, annotations, and proxy-based features like transactions.
  • When should I 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.
  • Can I inject 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.
  • Tricky: Does 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.
  • Tricky: Is 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.
  • Tricky: If I instantiate a class with 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:

  • Calling it just a registry. Correction: it is a full runtime container with lifecycle, events, and resource support.
  • Using new for Spring-managed classes. Correction: let the context create the object so injection and proxies work.
  • Using getBean() for every dependency. Correction: prefer constructor injection for fixed dependencies and reserve lookups for dynamic cases.
  • Thinking every bean is created immediately. Correction: singleton beans are eager by default, but lazy and prototype beans behave differently.

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.
  • It creates, wires, initializes, and destroys beans.
  • It adds extras: events, resources, messages, and environment access.
  • Spring Boot usually creates it with SpringApplication.run().
  • Bean lookup is fast; startup is the expensive part.
  • Use constructor injection first; use getBean() only when dynamic lookup is needed.

Practice Tasks:

  • Print all bean names in a tiny Boot app and see how many Spring creates for you.
  • Add two beans of the same interface and resolve the ambiguity with @Primary or by name.
  • Publish a custom event and handle it with @EventListener to feel the context in action.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

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()); } }