Spring instantiation source code analysis of Custom Events (Part 8)

Introduction to using Events

In the ApplicationContext, event handling is provided through the ApplicationEvent class and ApplicationListener interface. If a bean that implements the ApplicationListener interface is deployed into the context, the bean will be notified whenever an ApplicationEvent is posted to the ApplicationContext. Essentially, this is the standard observer design pattern. Official website address

Starting with Spring 4.2, the event infrastructure has been significantly improved and provides an annotation-based model and the ability to publish arbitrary events (i.e., objects that do not necessarily extend from ApplicationEvent). When publishing such an object, we wrap it into an event for you.

The following table describes the standard events provided by Spring:

Event Name Description
ContextRefreshedEvent Published when the ApplicationContext is initialized or refreshed. This usually happens when the container initialization is complete and ready to accept requests.
ContextStartedEvent Published when the context is started by calling the start() method of ApplicationContext.
ContextStoppedEvent Published when the context is stopped by calling the Stop() method of ApplicationContext.
ContextClosedEvent Published when the context is closed by calling the ApplicationContext’s close() method.
RequestHandledEvent In a web application, it is released when an HTTP request is processed.
ServletRequestHandledEvent In a Web application, it is released when an HTTP request is processed. It is similar to RequestHandledEvent, but provides more Servlet-related information.

These standard events provide tracking and handling capabilities for the application context’s lifecycle and request handling. You can also define and publish custom events to meet your application’s specific needs. The following example shows a simple class that extends Spring’s ApplicationEvent base class:

package com.qhyu.cloud.springEvent;

import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ApplicationContextEvent;

import java.io.Serializable;

/**
 * Title: BlockedListEvent<br>
 * Package: com.qhyu.cloud.springEvent<br>
 * @author piano<br>
 * date October 8, 2023 10:23<br>
 * @version v1.0<br>
 */
public class BlockedListEvent extends ApplicationContextEvent implements Serializable {<!-- -->

private static final long serialVersionUID = 1L;
private final String address;
private final String content;

/**
* Create a new ContextStartedEvent.
* @param source the {@code ApplicationContext} that the event is raised for
* (must not be {@code null})
*/
public BlockedListEvent(ApplicationContext source, String address, String content) {<!-- -->
super(source);
this.address = address;
this.content = content;
}

public String getAddress() {<!-- -->
return address;
}

public String getContent() {<!-- -->
return content;
}
}

To publish a custom ApplicationEvent, you can call the publishEvent() method on the ApplicationEventPublisher. Typically, this is accomplished by creating a class that implements the ApplicationEventPublisherAware interface and registering it as a Spring bean. The following example shows such a class:

package com.qhyu.cloud.springEvent;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

/**
 *Title: EmailService<br>
 * Package: com.qhyu.cloud.springEvent<br>
 * @author piano<br>
 * date October 8, 2023 10:28<br>
 * @version v1.0<br>
 */
@Component
public class EmailService implements ApplicationEventPublisherAware, ApplicationContextAware {<!-- -->

// Use the ApplicationEventPublisher application event publisher to publish events
private ApplicationEventPublisher applicationEventPublisher;
    
private ApplicationContext applicationContext;

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {<!-- -->
this.applicationEventPublisher = applicationEventPublisher;
}

public void sendEmail(String address, String content) {<!-- -->
applicationEventPublisher.publishEvent(new BlockedListEvent(applicationContext, address, content));
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {<!-- -->
this.applicationContext=applicationContext;
}
}

During configuration, the Spring container detects that EmailService implements the ApplicationEventPublisherAware interface and automatically calls the setApplicationEventPublisher() method. In fact, the parameter passed is the Spring container itself. You interact with the application context through its ApplicationEventPublisher interface.

To receive a custom ApplicationEvent, you can create a class that implements the ApplicationListener interface and register it as a Spring bean. The following example shows such a class:

package com.qhyu.cloud.springEvent;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * Title: BlockedListNotifier<br>
 * Package: com.qhyu.cloud.springEvent<br>
 * @author piano<br>
 * date October 8, 2023 10:33<br>
 * @version v1.0<br>
 */
@Component
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {<!-- -->

@Override
public void onApplicationEvent(BlockedListEvent event) {<!-- -->
System.out.println("Address:" + event.getAddress());
System.out.println("Content:" + event.getContent());
}
}

The result is as follows:

Note that the ApplicationListener is parameterized with generics to your custom event type (BlockedListEvent in the previous example). This means that the onApplicationEvent() method remains type safe and avoids any need for downcasting. You can register any number of event listeners, but note that by default, event listeners receive events synchronously. This means that the publishEvent() method will block until all listeners have finished processing the event. One advantage of this synchronous and single-threaded approach is that when a listener receives an event, it will run in the publisher’s transaction context, if one exists. If you need to use a different strategy for event publishing, such as asynchronous event handling by default, see the Javadoc for Spring’s ApplicationEventMulticaster interface and SimpleApplicationEventMulticaster implementation for configuration options that can be applied to custom “applicationEventMulticaster” bean definitions.

Spring’s event mechanism is designed to enable simple communication between Spring beans in the same application context. However, for more complex enterprise integration needs, the independently maintained Spring Integration project provides complete support for building solutions based on a lightweight, pattern-oriented, event-driven architecture built on the well-known Spring programming model above.

Source code analysis

initApplicationEventMulticaster

initApplicationEventMulticaster is a method in the Spring framework, used to initialize the application event broadcaster (ApplicationEventMulticaster).

In the Spring framework, events are published and propagated through application event broadcasters. When an event occurs, the broadcaster is responsible for notifying the event to listeners interested in the event.

The initApplicationEventMulticaster method is usually called during the context initialization process of a Spring application. Its role is to create and configure an application event broadcaster and register it with the Spring context.

Specifically, the initApplicationEventMulticaster method performs the following tasks:

  1. Create an application event broadcaster: Based on the configuration and context, create an appropriate application event broadcaster instance. In Spring, SimpleApplicationEventMulticaster is usually used as the default event broadcaster implementation.

  2. Configure the application event broadcaster: According to the configuration and requirements, perform some configurations on the application event broadcaster. For example, you can set the asynchronous execution mode of the broadcaster, task executor, exception handler, etc.

  3. Register application event broadcaster: Register the created and configured application event broadcaster into the Spring context so that it can be used when needed.

By initializing application event broadcasters, the Spring framework can implement an event-driven programming model. Developers can define their own event classes and write listeners to handle these events. Then, the event is propagated to the corresponding listener through the application event broadcaster, thereby achieving decoupling and collaboration between modules.

It should be noted that the initApplicationEventMulticaster method is usually automatically called by the Spring framework, and developers generally do not need to call this method manually. It is an initialization process inside the Spring container that ensures the correct configuration and registration of application event broadcasters.

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);
if (logger.isTraceEnabled()) {<!-- -->
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {<!-- -->
//Creation of broadcaster SimpleApplicationEventMulticaster
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {<!-- -->
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}

registerListeners

registerListeners is a method in the Spring framework for registering listeners with the application event broadcaster (ApplicationEventMulticaster).

In the Spring framework, events are published and propagated through application event broadcasters. Listeners are components used to receive and handle specific events. By registering a listener with the application event broadcaster, you can monitor and respond to events.

The registerListeners method is usually called during the context initialization process of a Spring application. Its function is to register the listeners defined in the application to the application event broadcaster so that it can receive and process the corresponding events.

Specifically, the registerListeners method performs the following tasks:

  1. Scan application context: Traverse all beans in the application context and find listener beans that implement the ApplicationListener interface.

  2. Register listener: Register the scanned listener bean to the application event broadcaster so that the corresponding event can be received when the event is published.

Through the registerListeners method, the Spring framework can automatically register listeners into the application event broadcaster without manually writing registration code. In this way, when an event occurs, the application event broadcaster will automatically notify the registered listener of the event, thereby triggering the processing logic defined in the listener.

It should be noted that the registerListeners method is usually automatically called by the Spring framework, and developers do not need to call this method manually. It is an initialization process inside the Spring container and is used to automatically register listeners. Developers only need to implement the corresponding listener interface, and then declare the listener bean in the Spring configuration to monitor and process events.

protected void registerListeners() {<!-- -->
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {<!-- -->
getApplicationEventMulticaster().addApplicationListener(listener);
}

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {<!-- -->
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}

// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {<!-- -->
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {<!-- -->
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}