Mastering Spring’s Observer Pattern: From Basics to Asynchronous Event Handling
This article explains the Observer pattern, its core roles and typical use cases, then dives into Spring's event mechanism—including ApplicationEvent, ApplicationListener, and ApplicationEventPublisher—showing how to configure synchronous and asynchronous event processing with code examples.
Observer Pattern Overview
The Observer pattern defines a one‑to‑many dependency between objects so that when the state of a subject changes, all its observers are automatically notified and updated. It is also known as publish‑subscribe or model‑view.
Key Roles
Subject (Observable) : declares the responsibilities that observers must implement, can dynamically add or remove observers, and notifies them.
Observer : receives notifications and performs update logic.
ConcreteSubject : implements the subject’s business logic and decides which events to publish.
ConcreteObserver : contains specific handling logic for received events.
Typical Scenarios
Objects have a one‑to‑many relationship where a state change in one affects many.
Two aspects of a model depend on each other and need independent reuse.
Broadcast‑style communication where the sender does not need to know the concrete listeners.
Layered or chained triggers that require cross‑domain notifications.
Observer Pattern in Spring
Spring Event Listening Mechanism
Spring implements the Observer pattern through its event system. An ApplicationEvent (extending EventObject) represents an event, while ApplicationListener (extending EventListener) defines the listener interface. Beans that implement ApplicationListener are automatically registered with the ApplicationEventMulticaster.
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp = System.currentTimeMillis();
public ApplicationEvent(Object source) {
super(source);
}
public final long getTimestamp() {
return this.timestamp;
}
} @FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}The ApplicationEventPublisher interface is implemented by ApplicationContext. The core publishing method in AbstractApplicationContext is:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
} else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// ... register early events or multicast directly ...
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
// propagate to parent context if present
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}The ApplicationEventMulticaster delivers events to all matching listeners. If an Executor is configured, delivery can be asynchronous:
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
} else {
invokeListener(listener, event);
}
}
}Initialization in AbstractApplicationContext
During context refresh, Spring initializes the event multicaster:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
} else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
}
}Listeners are registered via registerListeners(), which adds both bean‑defined listeners and those discovered by type.
protected void registerListeners() {
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// process early events if any
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}Asynchronous Event Handling
To enable async processing, provide a custom SimpleApplicationEventMulticaster with a configured TaskExecutor:
@Configuration
@EnableAsync
public class Config {
@Bean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(taskExecutor());
return multicaster;
}
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(300);
executor.setThreadNamePrefix("thread-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}Alternatively, the same configuration can be expressed in XML by defining a SimpleApplicationEventMulticaster bean and injecting a ThreadPoolTaskExecutor as its taskExecutor property.
Conclusion
The article covered the fundamentals of the Observer pattern, its roles, and typical use cases, then demonstrated how Spring implements this pattern through its event mechanism. By default Spring events are synchronous, but configuring a custom multicaster with a task executor enables efficient asynchronous processing.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD Cloud Developers
JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
