Complete guide to Spring Context: learn about ApplicationContext, its implementations, and how to access and manage application context in Spring applications. Master high-level container features and enterprise capabilities.
Table of Contents
1. What is ApplicationContext?
The ApplicationContext is Spring's advanced IoC container that extends the basic BeanFactory interface. It provides enterprise-level features and is the primary interface for accessing Spring's container functionality in most applications.
The following diagram illustrates the ApplicationContext hierarchy and its relationship to BeanFactory:
ApplicationContext provides:
- Bean factory capabilities (creating and managing beans)
- Automatic registration of BeanFactoryPostProcessor and BeanPostProcessor
- Internationalization (i18n) support through MessageSource
- Application event publishing and listening
- Resource loading capabilities
- Environment abstraction for profiles and properties
- Eager bean initialization (singleton beans are created at startup)
Unlike BeanFactory, ApplicationContext eagerly initializes singleton beans at startup, which helps detect configuration errors early but may increase startup time.
2. ApplicationContext vs BeanFactory
Understanding the differences between ApplicationContext and BeanFactory helps you choose the right container for your needs.
2.1 Key Differences
| Feature | BeanFactory | ApplicationContext |
|---|---|---|
| Bean Initialization | Lazy (on demand) | Eager (at startup) |
| Internationalization | No | Yes (MessageSource) |
| Application Events | No | Yes |
| Post Processors | Manual registration | Automatic registration |
| Resource Loading | Basic | Advanced (ResourceLoader) |
| Use Case | Memory-constrained environments | Most applications (recommended) |
2.2 When to Use Each
Use BeanFactory when:
- Memory is constrained
- You need lazy initialization
- You're building a lightweight application
Use ApplicationContext when:
- Building standard Spring applications (recommended)
- You need internationalization
- You want application events
- You need automatic post-processor registration
- You want early error detection
3. ApplicationContext Implementations
Spring provides several ApplicationContext implementations for different use cases.
3.1 ClassPathXmlApplicationContext
Loads XML configuration files from the classpath. Most common for standalone applications.
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml",
"services.xml",
"daos.xml"
);
// Or using array
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"applicationContext.xml", "services.xml"}
);
// Access beans
UserService userService = context.getBean(UserService.class);
UserService userServiceByName = (UserService) context.getBean("userService");
3.2 FileSystemXmlApplicationContext
Loads XML configuration files from the file system or URL.
// Load from file system
ApplicationContext context = new FileSystemXmlApplicationContext(
"/path/to/applicationContext.xml"
);
// Load from relative path
ApplicationContext context = new FileSystemXmlApplicationContext(
"conf/applicationContext.xml"
);
// Load from URL
ApplicationContext context = new FileSystemXmlApplicationContext(
"file:///path/to/applicationContext.xml"
);
3.3 AnnotationConfigApplicationContext
Uses Java-based configuration with @Configuration classes. Modern approach, no XML needed.
// Single configuration class
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// Multiple configuration classes
ApplicationContext context = new AnnotationConfigApplicationContext(
AppConfig.class,
DatabaseConfig.class,
SecurityConfig.class
);
// Component scanning
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
context.scan("com.example");
context.refresh();
// Or using constructor
ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
3.4 WebApplicationContext
Specialized ApplicationContext for web applications. Provides access to ServletContext.
// In web.xml (traditional)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
// Access in servlet
WebApplicationContext context =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
// In Spring Boot (automatic)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4. Context Features
4.1 Internationalization (i18n)
ApplicationContext provides built-in support for internationalization through the MessageSource interface.
// Configuration
@Configuration
public class AppConfig {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource =
new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
// messages.properties (default)
greeting=Hello
welcome=Welcome
// messages_fr.properties (French)
greeting=Bonjour
welcome=Bienvenue
// messages_es.properties (Spanish)
greeting=Hola
welcome=Bienvenido
// Usage
@Service
public class GreetingService {
private final MessageSource messageSource;
public GreetingService(MessageSource messageSource) {
this.messageSource = messageSource;
}
public String getGreeting(Locale locale) {
return messageSource.getMessage(
"greeting",
null,
locale
);
}
public String getWelcome(String name, Locale locale) {
return messageSource.getMessage(
"welcome",
new Object[]{name},
locale
);
}
}
// In controller
@RestController
public class GreetingController {
private final GreetingService greetingService;
public GreetingController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/greeting")
public String greeting(@RequestHeader("Accept-Language") String lang) {
Locale locale = Locale.forLanguageTag(lang);
return greetingService.getGreeting(locale);
}
}
4.2 Application Events
ApplicationContext supports a publish-subscribe event mechanism for loose coupling between components.
The event flow is illustrated below:
// Define custom event
public class OrderCreatedEvent extends ApplicationEvent {
private final Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
// Publish event
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public OrderService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void createOrder(Order order) {
// Save order
// ...
// Publish event
eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
}
}
// Listen to events
@Component
public class OrderEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
Order order = event.getOrder();
// Send notification, update inventory, etc.
System.out.println("Order created: " + order.getId());
}
@EventListener
@Async
public void sendEmail(OrderCreatedEvent event) {
// Async email sending
}
@EventListener(condition = "#event.order.total > 1000")
public void handleLargeOrder(OrderCreatedEvent event) {
// Handle large orders differently
}
}
// Multiple events
@Component
public class GenericEventListener {
@EventListener({OrderCreatedEvent.class, OrderCancelledEvent.class})
public void handleOrderEvents(ApplicationEvent event) {
// Handle multiple event types
}
}
4.3 Resource Loading
ApplicationContext implements ResourceLoader, providing unified resource access.
@Service
public class ResourceService {
private final ResourceLoader resourceLoader;
public ResourceService(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void loadResources() throws IOException {
// Load from classpath
Resource classpathResource =
resourceLoader.getResource("classpath:config.properties");
// Load from file system
Resource fileResource =
resourceLoader.getResource("file:/path/to/file.txt");
// Load from URL
Resource urlResource =
resourceLoader.getResource("https://example.com/data.json");
// Load with prefix
Resource resource = resourceLoader.getResource("classpath:data/input.txt");
// Check if resource exists
if (resource.exists()) {
InputStream inputStream = resource.getInputStream();
// Process resource
}
// Get file
File file = resource.getFile();
// Get URL
URL url = resource.getURL();
}
}
4.4 Environment Abstraction
ApplicationContext provides Environment abstraction for managing profiles and properties.
@Service
public class ConfigurationService {
private final Environment environment;
public ConfigurationService(Environment environment) {
this.environment = environment;
}
public void checkProfiles() {
// Check active profiles
String[] activeProfiles = environment.getActiveProfiles();
// Check if profile is active
if (environment.acceptsProfiles(Profiles.of("dev"))) {
// Dev-specific logic
}
// Get property
String dbUrl = environment.getProperty("database.url");
// Get property with default
int port = environment.getProperty("server.port", Integer.class, 8080);
// Get required property (throws exception if missing)
String apiKey = environment.getRequiredProperty("api.key");
// Check property existence
if (environment.containsProperty("feature.enabled")) {
boolean enabled = environment.getProperty("feature.enabled", Boolean.class);
}
}
}
// Access Environment in @Configuration
@Configuration
public class DatabaseConfig {
@Autowired
private Environment environment;
@Bean
public DataSource dataSource() {
String url = environment.getProperty("database.url");
String username = environment.getProperty("database.username");
String password = environment.getProperty("database.password");
// Create DataSource
return new HikariDataSource(/* ... */);
}
}
5. Accessing ApplicationContext
There are several ways to access ApplicationContext in your application.
5.1 Dependency Injection (Recommended)
The best way is to inject ApplicationContext or ApplicationContextAware.
@Service
public class UserService {
private final ApplicationContext context;
public UserService(ApplicationContext context) {
this.context = context;
}
public void doSomething() {
// Access beans programmatically
UserRepository repo = context.getBean(UserRepository.class);
}
}
// Or implement ApplicationContextAware
@Service
public class UserService implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
}
5.2 Static Access (Not Recommended)
You can access context statically, but this creates tight coupling.
public class ContextHolder {
private static ApplicationContext context;
public static void setContext(ApplicationContext context) {
ContextHolder.context = context;
}
public static ApplicationContext getContext() {
return context;
}
public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
}
// Initialize in configuration
@Configuration
public class AppConfig implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext context) {
ContextHolder.setContext(context);
}
}
5.3 WebApplicationContextUtils (Web Only)
In web applications, use WebApplicationContextUtils.
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletContext servletContext = request.getServletContext();
WebApplicationContext context =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = context.getBean(UserService.class);
}
}
6. Real-World Examples
6.1 Example 1: Multi-Context Application
// Parent context
ApplicationContext parentContext = new ClassPathXmlApplicationContext("parent-context.xml");
// Child context (inherits from parent)
ClassPathXmlApplicationContext childContext =
new ClassPathXmlApplicationContext();
childContext.setConfigLocation("child-context.xml");
childContext.setParent(parentContext);
childContext.refresh();
// Child can access parent beans, but not vice versa
Service parentService = childContext.getBean(Service.class); // From parent
ChildService childService = childContext.getBean(ChildService.class); // From child
6.2 Example 2: Programmatic Context Creation
@Configuration
public class DynamicContextExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
// Register configuration classes
context.register(AppConfig.class, DatabaseConfig.class);
// Register beans programmatically
context.registerBean("customService", CustomService.class,
() -> new CustomService("custom-value"));
// Refresh to initialize
context.refresh();
// Use context
CustomService service = context.getBean(CustomService.class);
// Close when done
context.close();
}
}
6.3 Example 3: Event-Driven Architecture
// Event hierarchy
public abstract class DomainEvent extends ApplicationEvent {
private final LocalDateTime occurredOn;
protected DomainEvent(Object source) {
super(source);
this.occurredOn = LocalDateTime.now();
}
public LocalDateTime getOccurredOn() {
return occurredOn;
}
}
public class UserRegisteredEvent extends DomainEvent {
private final User user;
public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() {
return user;
}
}
// Event publisher
@Service
public class UserService {
private final ApplicationEventPublisher eventPublisher;
private final UserRepository userRepository;
public UserService(ApplicationEventPublisher eventPublisher,
UserRepository userRepository) {
this.eventPublisher = eventPublisher;
this.userRepository = userRepository;
}
public void registerUser(User user) {
userRepository.save(user);
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
}
// Event listeners
@Component
public class UserEventHandlers {
@EventListener
@Order(1)
public void sendWelcomeEmail(UserRegisteredEvent event) {
// Send welcome email
}
@EventListener
@Order(2)
@Async
public void updateAnalytics(UserRegisteredEvent event) {
// Update analytics asynchronously
}
@EventListener
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void syncWithExternalSystem(UserRegisteredEvent event) {
// Sync only after transaction commits
}
}
6.4 Example 4: Internationalized Messages
@Configuration
public class I18nConfig {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setCacheSeconds(3600); // Cache for 1 hour
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.ENGLISH);
return resolver;
}
}
// Usage in service
@Service
public class MessageService {
private final MessageSource messageSource;
public MessageService(MessageSource messageSource) {
this.messageSource = messageSource;
}
public String getMessage(String code, Object[] args, Locale locale) {
return messageSource.getMessage(code, args, locale);
}
public String getMessage(String code, Locale locale) {
return messageSource.getMessage(code, null, locale);
}
}
// Usage in controller
@RestController
public class MessageController {
private final MessageService messageService;
public MessageController(MessageService messageService) {
this.messageService = messageService;
}
@GetMapping("/messages")
public String getMessage(
@RequestParam String code,
@RequestHeader(value = "Accept-Language", defaultValue = "en") String lang) {
Locale locale = Locale.forLanguageTag(lang);
return messageService.getMessage(code, locale);
}
}
7. Best Practices
7.1 Prefer Dependency Injection
Always inject ApplicationContext or specific beans rather than accessing context statically.
7.2 Use Appropriate Context Implementation
Choose the right ApplicationContext implementation for your use case (AnnotationConfigApplicationContext for Java config, ClassPathXmlApplicationContext for XML).
7.3 Leverage Application Events
Use events for loose coupling between components instead of direct dependencies.
7.4 Use Environment for Configuration
Use Environment abstraction for accessing properties and managing profiles.
7.5 Close Context Properly
Always close ApplicationContext in standalone applications to release resources.
try (AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class)) {
// Use context
} // Automatically closed
8. Testing
Spring provides excellent support for testing with ApplicationContext.
8.1 Integration Testing
@SpringJUnitConfig(AppConfig.class)
class UserServiceIntegrationTest {
@Autowired
private ApplicationContext context;
@Autowired
private UserService userService;
@Test
void testContextLoads() {
assertNotNull(context);
assertTrue(context.containsBean("userService"));
}
@Test
void testBeanRetrieval() {
UserService service = context.getBean(UserService.class);
assertNotNull(service);
assertSame(userService, service); // Singleton
}
}
8.2 Testing with Profiles
@SpringJUnitConfig(AppConfig.class)
@ActiveProfiles("test")
class ProfileTest {
@Autowired
private Environment environment;
@Test
void testProfileActive() {
assertTrue(environment.acceptsProfiles(Profiles.of("test")));
}
}
8.3 Testing Events
@SpringJUnitConfig(AppConfig.class)
class EventTest {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private TestEventListener testListener;
@Test
void testEventPublishing() {
OrderCreatedEvent event = new OrderCreatedEvent(this, new Order());
eventPublisher.publishEvent(event);
assertTrue(testListener.isEventReceived());
}
}
9. Advanced Concepts
9.1 Hierarchical Contexts
Spring supports parent-child context relationships for modular applications.
// Parent context
ApplicationContext parent = new ClassPathXmlApplicationContext("parent.xml");
// Child context
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext();
child.setConfigLocation("child.xml");
child.setParent(parent);
child.refresh();
// Child can access parent beans
ParentBean parentBean = child.getBean(ParentBean.class);
ChildBean childBean = child.getBean(ChildBean.class);
9.2 Refreshable Context
Some ApplicationContext implementations support refreshing to reload configuration.
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("config.xml");
// Use context
// ...
// Refresh to reload configuration
context.refresh();
// Close when done
context.close();
9.3 Custom ApplicationContext
You can create custom ApplicationContext implementations for special requirements.
public class CustomApplicationContext extends GenericApplicationContext {
@Override
protected void refreshBeanFactory() throws IllegalStateException {
// Custom initialization logic
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// Configure bean factory
setBeanFactory(beanFactory);
}
@Override
protected void closeBeanFactory() {
// Custom cleanup logic
super.closeBeanFactory();
}
}
0 Comments