SoFunction
Updated on 2025-04-15

Detailed explanation of the event release and listening mechanism of SpringBoot ApplicationEvent

introduction

In modern enterprise-level application development, decoupling and flexible communication between various components is becoming increasingly important. Spring Framework provides a powerful event mechanism that allows various components in an application to interact in a loosely coupled manner.

SpringBoot inherits and enhances this mechanism, providing developers with an elegant event publishing and listening framework through ApplicationEvent and its related components.

1. Basic concepts of event mechanism

Spring's event mechanism is based on the observer design pattern and is mainly composed of three core components: event (Event), event publisher (Publisher) and event listener (Listener). Its workflow is: the event publisher publishes a specific type of event, the system passes the event to all listeners interested in this type of event, and the listener then executes the corresponding processing logic.

The biggest advantage of this mechanism is that it realizes decoupling between the event publisher and the event handler, making the system more modular and easy to expand and maintain. In SpringBoot, this mechanism is implemented through the ApplicationEvent class and related interfaces.

2. Create custom events

2.1 Defining event classes

Custom events need to inherit SpringBoot's ApplicationEvent class, which defines the basic properties and behavior of events:

package ;

import ;

/**
  * User registration event
  * Published after user registration is successful, used to perform subsequent operations
  */
public class UserRegisteredEvent extends ApplicationEvent {
    
    // User ID    private final Long userId;
    // username    private final String username;
    
    /**
      * Constructor
      * @param source Event source (object to publish the event)
      * @param userId Registered user ID
      * @param username Registered username
      */
    public UserRegisteredEvent(Object source, Long userId, String username) {
        super(source);
         = userId;
         = username;
    }
    
    public Long getUserId() {
        return userId;
    }
    
    public String getUsername() {
        return username;
    }
}

This example defines a user registration event, which is published after the user registration is successful, carries the user ID and user name information, and can be listened to and responded to by other components.

2.2 Publish events

In SpringBoot, publishing events are mainly done through the ApplicationEventPublisher interface, which is usually obtained through dependency injection:

package ;

import ;
import ;
import ;
import ;
import ;

/**
  * User Service
  * Responsible for user-related business logic and publish events at the appropriate time
  */
@Service
public class UserService implements ApplicationEventPublisherAware {
    
    private ApplicationEventPublisher eventPublisher;
    
    /**
      * Methods to implement ApplicationEventPublisherAware interface
      * Spring will automatically inject ApplicationEventPublisher
      */
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
         = eventPublisher;
    }
    
    /**
      * User registration method
      * @param username Username
      * @param password
      * @return User object successfully registered
      */
    public User registerUser(String username, String password) {
        // Execute user registration logic        User newUser = saveUser(username, password);
        
        // After successful registration, publish a user registration event        (new UserRegisteredEvent(this, (), ()));
        
        return newUser;
    }
    
    /**
      * Save user information (simulation method)
      */
    private User saveUser(String username, String password) {
        // In actual project, user information will be saved to the database        User user = new User();
        (1L); // Simulation ID        (username);
        // Omit password encryption and other secure processing        return user;
    }
}

In addition to implementing the ApplicationEventPublisherAware interface, you can also directly inject ApplicationEventPublisher:

@Service
public class AlternativeUserService {
    
    private final ApplicationEventPublisher eventPublisher;
    
    @Autowired
    public AlternativeUserService(ApplicationEventPublisher eventPublisher) {
         = eventPublisher;
    }
    
    public User registerUser(String username, String password) {
        // Registration logic        User newUser = saveUser(username, password);
        
        // Publish an event        (new UserRegisteredEvent(this, (), ()));
        
        return newUser;
    }
    
    // Other methods...}

2.3 Simplified event release

Starting from Spring 4.2, you can also directly publish any object as an event, and Spring will automatically wrap it as a PayloadApplicationEvent:

@Service
public class SimpleUserService {
    
    private final ApplicationEventPublisher eventPublisher;
    
    @Autowired
    public SimpleUserService(ApplicationEventPublisher eventPublisher) {
         = eventPublisher;
    }
    
    public User registerUser(String username, String password) {
        // Registration logic        User newUser = saveUser(username, password);
        
        // Publish the object directly as an event        (newUser);
        
        return newUser;
    }
    
    // Other methods...}

3. Create an event listener

SpringBoot provides a variety of ways to create event listeners, and here are a few common methods:

3.1 Annotation with @EventListener

The simplest way is to use @EventListener annotation:

package ;

import ;
import org.;
import org.;
import ;
import ;

/**
  * User registration event listener
  * Responsible for handling the operation after user registration
  */
@Component
public class UserRegistrationListener {
    
    private static final Logger logger = ();
    
    /**
      * Handle user registration events
      * @param event User registration event
      */
    @EventListener
    public void handleUserRegistration(UserRegisteredEvent event) {
        ("New user registration: ID={}, username={}", (), ());
        
        // Perform the user registration operation, for example:        // 1. Send a welcome email        sendWelcomeEmail(());
        
        // 2. Create default user settings        createDefaultUserSettings(());
        
        // 3. Record user registration statistics        updateRegistrationStatistics();
    }
    
    private void sendWelcomeEmail(String username) {
        ("Send a welcome email to: {}", username);
        // Email sending logic    }
    
    private void createDefaultUserSettings(Long userId) {
        ("For users {} Create default settings", userId);
        // Create default settings logic    }
    
    private void updateRegistrationStatistics() {
        ("Update user registration statistics");
        // Update statistics logic    }
}

3.2 Implementing the ApplicationListener interface

Another way is to implement the ApplicationListener interface:

package ;

import ;
import org.;
import org.;
import ;
import ;

/**
  * Event listener using ApplicationListener interface
  */
@Component
public class EmailNotificationListener implements ApplicationListener<UserRegisteredEvent> {
    
    private static final Logger logger = ();
    
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        ("ApplicationListener: Handle user registration event");
        
        // Execute event processing logic        String emailContent = (
            "Dear %s, welcome to register for our service! Your account has been created successfully.",
            ()
        );
        
        ("The content of the email you are ready to send: {}", emailContent);
        // The code to actually send the email...    }
}

3.3 Listening for events that are not of ApplicationEvent type

As mentioned earlier, starting with Spring 4.2, you can listen for objects of any type:

@Component
public class GenericEventListener {
    
    private static final Logger logger = ();
    
    @EventListener
    public void handleUserEvent(User user) {
        ("ReceivedUserObject Events: {}", ());
        // Processing logic    }
    
    // Multiple listening methods can be added, each method handles different types of events    @EventListener
    public void handleOrderEvent(Order order) {
        ("ReceivedOrderObject Events: ID={}", ());
        // Processing logic    }
}

4. Advanced features of event monitoring

4.1 Conditional event monitoring

You can use SpEL expression to specify the conditions for event listening:

@Component
public class ConditionalEventListener {
    
    private static final Logger logger = ();
    
    /**
      * Conditional event listening - Only handles registration events for VIP users
      */
    @EventListener(condition = "# == 'VIP'")
    public void handleVipUserRegistration(UserRegisteredEvent event) {
        ("deal withVIPUser registration: {}", ());
        // VIP user special processing logic    }
}

4.2 Asynchronous event listening

By default, event processing is synchronized and may block publishers. By adding @Async annotation, asynchronous event processing can be implemented:

package ;

import ;
import ;

@Configuration
@EnableAsync  // Enable asynchronous supportpublic class AsyncConfig {
    // Asynchronous configuration...}
package ;

import ;
import org.;
import org.;
import ;
import ;
import ;

/**
  * Asynchronous event listener
  */
@Component
public class AsyncEventListener {
    
    private static final Logger logger = ();
    
    /**
      * Asynchronously handle user registration events
      * Suitable for time-consuming operations to avoid blocking the main thread
      */
    @EventListener
    @Async
    public void handleUserRegistrationAsync(UserRegisteredEvent event) {
        ("Asynchronously handle user registration events,Thread: {}", ().getName());
        
        try {
            // Simulation time-consuming operation            (2000);
            ("Complete user {} Asynchronous processing", ());
        } catch (InterruptedException e) {
            ().interrupt();
            ("Async processing is interrupted", e);
        }
    }
}

4.3 Sequence control of event listening

When multiple listeners handle the same event, you can use the @Order annotation to control the execution order:

@Component
public class OrderedEventListeners {
    
    private static final Logger logger = ();
    
    @EventListener
    @Order(1)  // The highest priority is executed first    public void handleUserRegistrationFirst(UserRegisteredEvent event) {
        ("The first step to deal with it: {}", ());
        // Processing logic    }
    
    @EventListener
    @Order(2)  // Second execution    public void handleUserRegistrationSecond(UserRegisteredEvent event) {
        ("Step 2 Processing: {}", ());
        // Processing logic    }
    
    @EventListener
    @Order(Integer.MAX_VALUE)  // The lowest priority is executed last    public void handleUserRegistrationLast(UserRegisteredEvent event) {
        ("Final steps to process: {}", ());
        // Processing logic    }
}

4.4 Transaction event monitoring

In a transaction environment, certain operations may need to be performed after the transaction is successfully committed. Spring provides the @TransactionalEventListener annotation:

package ;

import ;
import org.;
import org.;
import ;
import ;
import ;

/**
  * Transaction event listener
  */
@Component
public class TransactionalListener {
    
    private static final Logger logger = ();
    
    /**
      * Transaction submission process events
      * Ensure that subsequent operations are performed only after the transaction is successfully committed
      */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleUserRegistrationAfterCommit(UserRegisteredEvent event) {
        ("Processing user registration after transaction submission: {}", ());
        // For example: Send a message to an external system, this operation should only be executed after the transaction is successful    }
    
    /**
      * Transaction rollback after processing events
      */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleUserRegistrationAfterRollback(UserRegisteredEvent event) {
        ("Transaction rollback after processing: {}", ());
        // For example: record the cause of failure, send alarms, etc.    }
}

5. Spring built-in events

Spring provides a variety of built-in events that are automatically published at specific moments:

5.1 Common built-in events

  • ContextRefreshedEvent: Published when ApplicationContext is initialized or refreshed
  • ContextStartedEvent: Published when ApplicationContext starts
  • ContextStoppedEvent: Published when ApplicationContext stops
  • ContextClosedEvent: Published when ApplicationContext is closed

5.2 Sample listening for built-in events

package ;

import org.;
import org.;
import ;
import ;
import ;

/**
  * Spring built-in event listener
  */
@Component
public class SystemEventListener {
    
    private static final Logger logger = ();
    
    /**
      * Listen to the application context refresh event
      * Suitable for initialization after application startup
      */
    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        ("Application context refreshed,applicationID: {}", ().getId());
        
        // Execute system initialization logic        // For example: preload cache, initialize resources, etc.        initializeSystemResources();
    }
    
    private void initializeSystemResources() {
        ("Initialize system resources...");
        // Initialization code    }
}

Summarize

Spring Boot's event mechanism provides applications with powerful inter-component communication capabilities, allowing developers to build loosely coupled, highly cohesive systems. Through event release and listening, business logic can be reasonably divided, the main process focuses on core operations, while secondary or auxiliary operations are processed asynchronously through events, thereby improving the maintainability and scalability of the system.

In actual applications, the event mechanism is especially suitable for the following scenarios: sending a welcome email after the user registers, inventory checks after the order is created, updates cache after data changes, and sending notifications after the business operations are completed. These operations are related to the main process but are relatively independent, and the code structure can be clearer through event mechanism processing.

With the popularization of microservice architecture, Spring Boot's event mechanism can also be combined with technologies such as message queues to realize a cross-service event-driven architecture. Developers can use ApplicationEvent to handle local events within the service, and for scenarios where cross-service communication is required, they can forward events to message queues to achieve a larger scope of decoupling.

The above is personal experience. I hope you can give you a reference and I hope you can support me more.