Spring's ApplicationContext stands as a cornerstone of the framwork, acting as the central container for managing beans and providing a rich set of services to applications. Beyond its primary role as a bean factory, it inherits capabilities from several key interfaces, extending its functionality across various domains. Understanding these inherited interfaces is crucial for fully leveraging the power of the Spring container.
MessageSource for Internationalization
The MessageSource interface equips the ApplicationContext with robust internationalization (i18n) and localization (l10n) features. This allows applications to externalize messages, making it easy to support multiple languages and regions without changing the core code. To utilize this, you typically define mesage properties files (e.g., messages_en.properties, messages_fr.properties) in your classpath.
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import java.util.Locale;
@Configuration
public class ApplicationConfiguration {
// Basic configuration class, no beans explicitly defined for this example
}
public class I18nExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
// Define a message in resources/messages_en.properties: app.greeting=Hello from Spring!
String welcomeMessage = context.getMessage("app.greeting", null, new Locale("en"));
System.out.println("English Message: " + welcomeMessage);
// Assuming messages_fr.properties: app.greeting=Bonjour de Spring!
String frenchMessage = context.getMessage("app.greeting", null, new Locale("fr"));
System.out.println("French Message: " + frenchMessage);
context.close();
}
}
For the above example, you would need a messages_en.properties file in your src/main/resources directory:
app.greeting=Hello from Spring!
And optionally, a messages_fr.properties file:
app.greeting=Bonjour de Spring!
ResourcePatternResolver for Unified Resource Access
The ResourcePatternResolver interface provides a unified API for loading resources from various locations, such as the classpath, file system, or URLs. This abstracts away the underlying resource access mechanism, allowing developers to work with resources consistently.
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Arrays;
@Configuration
public class ResourceConfig {
// Configuration class
}
public class ResourceLoadingExample {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext(ResourceConfig.class);
// Loading a single resource from the classpath
// Assuming a file named 'application.properties' exists in src/main/resources
Resource classpathResource = appContext.getResource("classpath:application.properties");
if (classpathResource.exists()) {
System.out.println("Classpath resource found: " + classpathResource.getDescription());
System.out.println("Length: " + classpathResource.contentLength() + " bytes");
}
// Loading a resource from a URL
Resource urlResource = appContext.getResource("https://www.example.com");
if (urlResource.exists()) {
System.out.println("\nURL resource found: " + urlResource.getDescription());
System.out.println("Length: " + urlResource.contentLength() + " bytes");
System.out.println("URL: " + urlResource.getURL());
}
// Loading multiple resources using a pattern (e.g., all .class files in a package)
// Adjust the path 'com/example/app/*' based on your project structure
Resource[] matchingResources = appContext.getResources("classpath*:com/example/app/*.class");
System.out.println("\nMatching classpath resources:");
Arrays.stream(matchingResources).forEach(res -> System.out.println("- " + res.getDescription()));
appContext.close();
}
}
EnvironmentCapable for Accessing the Application Environment
The EnvironmentCapable interface provides access to the application's environment, which includes profiles, properties, and system settings. This is crucial for configuring applications based on deployment environments (development, test, production) and for injecting externalized configuration values.
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import java.util.Arrays;
@Configuration
@PropertySource("classpath:app-settings.properties") // Load properties from app-settings.properties
public class EnvironmentConfig {
// Configuration class
}
public class EnvironmentAccessExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EnvironmentConfig.class);
Environment env = context.getEnvironment();
System.out.println("--- Active Profiles ---");
Arrays.stream(env.getActiveProfiles()).forEach(System.out::println);
System.out.println("\n--- Property Sources ---");
env.getPropertySources().forEach(ps -> System.out.println(ps.getName() + " -> " + ps.getSource().getClass().getSimpleName()));
System.out.println("\n--- System Environment Variables ---");
env.getSystemEnvironment().forEach((key, value) -> System.out.println(key + " : " + value));
System.out.println("\n--- System Properties ---");
env.getSystemProperties().forEach((key, value) -> System.out.println(key + " : " + value));
// Accessing a custom property loaded via @PropertySource or application.properties
// Assuming app-settings.properties contains: app.version=1.0.0
System.out.println("\nCustom App Version: " + env.getProperty("app.version"));
// Accessing a standard system property
System.out.println("Java Home: " + env.getProperty("java.home"));
context.close();
}
}
For this example, you would need an app-settings.properties file in your src/main/resources directory:
app.version=1.0.0
config.name=production-settings
ApplicationEventPublisher for Event-Driven Communicasion
The ApplicationEventPublisher interface enables event-driven communication within a Spring application. This provides a clean way to decouple components by allowing them to publish events and react to them without direct dependencies. The ApplicationContext itself serves as an event publisher.
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
// 1. Define a custom application event
class CustomApplicationEvent extends ApplicationEvent {
private final String content;
public CustomApplicationEvent(Object source, String content) {
super(source);
this.content = content;
}
public String getContent() {
return content;
}
}
// 2. Define an event listener
@Component
class CustomEventListener implements ApplicationListener<CustomApplicationEvent> {
@Override
public void onApplicationEvent(CustomApplicationEvent event) {
System.out.println("Received custom event: " + event.getContent() + " from source: " + event.getSource().getClass().getSimpleName());
}
}
@ComponentScan // Scans for components like CustomEventListener
@Configuration
class EventConfig {
// Configuration class
}
public class EventPublisherExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);
// Publish a custom event using the ApplicationContext
context.publishEvent(new CustomApplicationEvent(new Object(), "User login successful!"));
context.close();
}
}
Bean Ordering with OrderComparator and AnnotationAwareOrderComparator
While not an inherited interface of ApplicationContext, Spring provides powerful comparator utilities like OrderComparator and AnnotationAwareOrderComparator to manage the processing order of beans. These are crucial when the sequence of operations or bean initialization matters, often used internally by Spring or when you need to sort collections of ordered components.
Components can implement the org.springframework.core.Ordered interface or be annotated with @org.springframework.core.annotation.Order to specify their relative order. Lower values indicate higher priority.
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.core.OrderComparator;
import java.util.Arrays;
import java.util.List;
// Class implementing Ordered interface
class TaskProcessorAlpha implements Ordered {
@Override
public int getOrder() {
return 2; // Lower value means higher priority
}
@Override
public String toString() { return "TaskProcessorAlpha (Order: 2)"; }
}
// Class using @Order annotation
@Order(1) // Higher priority than TaskProcessorAlpha
class TaskProcessorBeta {
@Override
public String toString() { return "TaskProcessorBeta (Order: 1)"; }
}
class TaskProcessorGamma implements Ordered {
@Override
public int getOrder() {
return 3;
}
@Override
public String toString() { return "TaskProcessorGamma (Order: 3)"; }
}
public class OrderComparatorExample {
public static void main(String[] args) {
// Using OrderComparator for objects implementing Ordered
List<Ordered> orderedTasks = Arrays.asList(
new TaskProcessorAlpha(),
new TaskProcessorGamma()
);
System.out.println("--- Sorted using OrderComparator (Ordered interface) ---");
orderedTasks.stream().sorted(OrderComparator.INSTANCE).forEach(System.out::println);
System.out.println("\n--- Sorted using AnnotationAwareOrderComparator (@Order annotation & Ordered interface) ---");
List<Object> mixedTasks = Arrays.asList(
new TaskProcessorAlpha(),
new TaskProcessorBeta(),
new TaskProcessorGamma()
);
mixedTasks.stream().sorted(AnnotationAwareOrderComparator.INSTANCE).forEach(System.out::println);
}
}