The ApplicationContext is Spring’s primary Inversion of Control container. It creates application components, resolves dependencies, manages bean lifecycles and provides the foundation for Spring features such as events, configuration properties, AOP and transaction management.
In a modern Spring application, however, developers rarely create an ApplicationContext manually or call getBean() throughout their business code. Spring Boot creates the context at startup, component scanning and configuration register the beans, and constructor injection supplies those beans where they are needed.
This article uses Spring Boot 4.1, Spring Framework 7 and Java 25.
What is the Spring ApplicationContext?
A Spring ApplicationContext is a registry and lifecycle manager for Spring beans.
A Spring bean is simply an object managed by the Spring IoC container. Bean definitions and configuration metadata tell Spring:
- which objects should become beans;
- how those objects should be created;
- which dependencies they require;
- which bean scope applies;
- when lifecycle callbacks should run; and
- which Spring infrastructure should participate in their creation.
Spring Framework 7 continues to position ApplicationContext as the normal application-facing container. It builds on BeanFactory and adds application-level features such as event publication, resource handling and lifecycle integration.
Spring Boot creates the ApplicationContext
A Spring Boot application usually starts with SpringApplication.run():
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}Spring Boot 4.1 creates and refreshes an appropriate ApplicationContext, performs component scanning, applies auto-configuration and starts the application.
The returned object is a ConfigurableApplicationContext:
var context = SpringApplication.run(App.class, args);That reference is useful for infrastructure code, tests and advanced bootstrapping, but most application classes should not keep a reference to the context.
The modern equivalent of ApplicationContext.getBean()
Older Spring tutorials often demonstrate dependency lookup like this:
var happyMeal = context.getBean(HappyMeal.class);The call is valid, but using getBean() throughout business code turns the Spring container into a service locator and couples application logic directly to Spring.
The modern equivalent is constructor injection.
import org.springframework.stereotype.Component;
@Component
public class HappyMeal {
private final Drink drink;
public HappyMeal(Drink drink) {
this.drink = drink;
}
public String description() {
return "Happy Meal with " + drink.name();
}
}A service that depends on HappyMeal simply declares the dependency:
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final HappyMeal happyMeal;
public OrderService(HappyMeal happyMeal) {
this.happyMeal = happyMeal;
}
public void placeOrder() {
IO.println(happyMeal.description());
}
}Spring resolves the dependency while it creates OrderService. No manual bean lookup is required.
Component scanning and Spring stereotypes
@SpringBootApplication includes component scanning. Classes in the application’s base package can be discovered automatically when they use Spring stereotypes such as:
@Componentfor a general managed component;@Servicefor service-layer code;@Repositoryfor persistence components;@Controllerfor Spring MVC controllers; and@RestControllerfor REST controllers.
import org.springframework.stereotype.Component;
@Component
public class Cola implements Drink {
@Override
public String name() {
return "Cola";
}
}Once Spring discovers both Cola and HappyMeal, it can inject the Drink dependency automatically.
@Configuration and @Bean are the modern XML equivalent
Not every bean should be discovered with @Component. When object creation requires explicit setup, or when a type comes from a third-party library, Java configuration is usually the better choice.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MealConfiguration {
@Bean
Drink drink() {
return new Cola();
}
@Bean
HappyMeal happyMeal(Drink drink) {
return new HappyMeal(drink);
}
}Method parameters express dependencies, and Spring supplies matching beans from the context.
@Configuration(proxyBeanMethods = false) is useful when the configuration class does not call its own @Bean methods directly. This avoids CGLIB interception of inter-bean method calls.
XML configuration vs modern Spring configuration
Spring still supports XML bean definitions, but XML is no longer the default style for new Spring Boot applications.
An older configuration might look like this:
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="drink"
class="com.mcnz.spring.Cola"/>
<bean id="happyMeal"
class="com.mcnz.spring.HappyMeal">
<constructor-arg ref="drink"/>
</bean>
</beans>The Java configuration equivalent is type-safe and easier to refactor:
@Configuration(proxyBeanMethods = false)
class MealConfiguration {
@Bean
Drink drink() {
return new Cola();
}
@Bean
HappyMeal happyMeal(Drink drink) {
return new HappyMeal(drink);
}
}XML remains supported and can still make sense in legacy systems or specific integration scenarios, but application size by itself is not a reason to prefer XML.
Modern configuration with @ConfigurationProperties
Older explanations often describe the ApplicationContext as a way to access property files. In Spring Boot, the modern equivalent is externalized configuration combined with @ConfigurationProperties.
meal:
drink: Cola
size: LargeBind the configuration to a Java record:
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("meal")
public record MealProperties(
String drink,
String size
) {
}Once registered, MealProperties can be injected like any other bean. This is cleaner than manually asking the Environment or ApplicationContext for individual values throughout business code.
Application events
The ApplicationContext also acts as an application event publisher.
public record OrderPlaced(long orderId) {
}import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service
public class CheckoutService {
private final ApplicationEventPublisher events;
public CheckoutService(ApplicationEventPublisher events) {
this.events = events;
}
public void checkout(long orderId) {
events.publishEvent(new OrderPlaced(orderId));
}
}import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class OrderListener {
@EventListener
void onOrderPlaced(OrderPlaced event) {
IO.println("Order placed: " + event.orderId());
}
}This demonstrates an important modern pattern: application code benefits from services provided by the context without directly querying the context itself.
Bean lifecycle management
Spring manages the lifecycle of beans it creates. Modern applications commonly use Jakarta lifecycle annotations:
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;
@Component
public class CacheManager {
@PostConstruct
void initialize() {
IO.println("Cache initialized");
}
@PreDestroy
void shutdown() {
IO.println("Cache shut down");
}
}Spring bean scopes include singleton, prototype, request, session, application and websocket scopes, with the web-specific scopes available in a web-aware ApplicationContext.
ApplicationContext vs BeanFactory
ApplicationContext extends BeanFactory, but it is inaccurate to describe BeanFactory as obsolete.
BeanFactory remains the foundational Spring IoC contract and is still important inside Spring itself and in framework integration code. Spring recommends ApplicationContext for normal application development because it adds lifecycle integration, event publication, automatic post-processor detection and other higher-level capabilities.
Direct use of a plain BeanFactory is mainly appropriate for specialized infrastructure or framework code that needs lower-level control.
ApplicationContext implementations you may encounter
Most Spring Boot developers do not need to choose an ApplicationContext implementation. Spring Boot selects an appropriate context based on the type of application.
Outside Boot, or in framework-level code, you may encounter:
AnnotationConfigApplicationContextfor annotation and Java configuration;GenericApplicationContextfor flexible programmatic registration;- web-aware contexts used by Spring MVC and WebFlux infrastructure;
ClassPathXmlApplicationContextfor classpath XML configuration; andFileSystemXmlApplicationContextfor file-system XML configuration.
Programmatic bean registration
Spring can also register beans programmatically. This is useful in infrastructure code and highly dynamic applications.
import org.springframework.context.support.GenericApplicationContext;
void createContext() {
var context = new GenericApplicationContext();
context.registerBean(
Drink.class,
Cola::new
);
context.registerBean(
HappyMeal.class,
() -> new HappyMeal(context.getBean(Drink.class))
);
context.refresh();
var meal = context.getBean(HappyMeal.class);
IO.println(meal.description());
context.close();
}This is valid Spring, but it is not a replacement for constructor injection in ordinary business code.
Spring Boot 4.1 and Java 25
Spring Boot 4.1 requires Java 17 or newer and supports Java through version 26, so Java 25 is fully supported. Spring Boot 4.1 is built on Spring Framework 7.
A minimal Maven project can target Java 25 directly:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.0</version>
</parent>
<properties>
<java.version>25</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>Spring Boot manages compatible Spring Framework and third-party dependency versions. Application builds generally should not override individual Spring Framework versions unless there is a specific reason.
Modern Spring ApplicationContext best practices
- Let Spring Boot create the
ApplicationContext. - Prefer constructor injection over manual
getBean()calls. - Use Spring stereotypes for application components discovered by scanning.
- Use
@Configurationand@Beanfor explicit or third-party object creation. - Use
@ConfigurationPropertiesfor structured external configuration. - Use application events when components need loose event-driven communication.
- Use XML mainly for legacy applications or integrations that genuinely benefit from it.
- Use
ApplicationContextrather than a rawBeanFactoryfor normal application code.
The modern way to think about the Spring ApplicationContext is not as an object your business code continually asks for dependencies. It is the runtime container that assembles the application, wires the object graph and then stays mostly out of the way while your beans collaborate through ordinary Java references.

Cameron McKenzie is an AWS Certified AI Practitioner, Machine Learning Engineer, Solutions Architect and author of many popular books in the software development and Cloud Computing space. His growing YouTube channel training devs in Java, Spring, AI and ML has well over 30,000 subscribers.