Spring event publishing mechanism

Table of contents

event driven

Use the event mechanism

Java event usage

Spring event usage

Use Aware

Not using Aware

Spring event publishing process and source code analysis

ApplicationEvent

ApplicationListener

listener registration

ApplicationEventPublisher

AbstractApplicationContext

ApplicationEventMulticaster

retrieveApplicationListeners

The process of event publishing

The event idea of ​​​​the observer pattern

        The Observer pattern is implemented as a one-to-many dependency between objects. In the observer mode, the observed is equivalent to the time publisher in the event, and the observer is equivalent to the listener in the event. Therefore, it can be said that the observer mode is a manifestation of the event-driven mechanism.

event driven

        A common form of event-driven is the publish-subscribe model. In cross-process communication, we often use message queues to implement message publishing and subscription. In the current mainstream framework, the publish-subscribe model of messages is used to decouple large-scale distributed projects. It makes the data producer and sender separate, and MQ can also play the role of peak cutting. Many times in the same process, this event-driven mechanism is also needed for decoupling

Use the event mechanism

The event mechanism is mainly composed of three parts: event source, event object, listener

  • Event source: the origin of the event

  • Event object: event entity, the event object will hold an event source

  • Listener: Listen to the event object and process the event object

Java event usage

Java provides two interfaces related to events:

  • EventObject: event object, custom events need to inherit this class

  • EventListener: event listener interface

Since the event source Source does not need to implement any interface, no corresponding definition is given in Java

An example of implementing events natively in Java:

import java.util.EventObject;

/**
 * @Author: chenyang
 * @DateTime: 2022/9/21 10:08
 * @Description: 事件对象
 */
public class JavaEvent extends EventObject {

    private String msg;


    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public JavaEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}
import com.yang.common.event.JavaEvent;
import java.util.EventListener;

/**
 * @Author: chenyang
 * @DateTime: 2022/9/21 10:09
 * @Description: 事件监听者,按照 Java 规范应实现 EventListener 接口
 */
public class JavaListener implements EventListener {

    public void handlerEvent(JavaEvent event){
        System.out.println("Java Event msg : " + event.getMsg());
    }
}
import com.yang.common.event.JavaEvent;
import com.yang.common.listener.JavaListener;
import java.util.EventListener;
import java.util.HashSet;

/**
 * @Author: chenyang
 * @DateTime: 2022/9/21 10:12
 * @Description: 事件源
 */
public class JavaSource {
    private static HashSet<EventListener> set = new HashSet<>();

    public void addListener(EventListener listener){
        set.add(listener);
    }

    public void publishEvent(JavaEvent event){
        for (EventListener listener : set) {
            ((JavaListener)listener).handlerEvent(event);
            
        }
    }
}
public class Main {
    public static void main(String[] args) {
        JavaSource source = new JavaSource();
        JavaListener listener = new JavaListener();
        source.addListener(listener);
        source.publishEvent(new JavaEvent(source, "SAY MY NAME !!!"));
    }
}

Spring event usage

Spring provides event-related interfaces and classes. In Spring, events can be published-subscribed by implementing interfaces. Spring's event mechanism is based on Java's event mechanism and is extended on demand.

Event-related definitions in Spring are as follows:

  • ApplicationEvent: Inherit the ObjectEvent class, and the event source should inherit this class.

  • ApplicationListener: Event listener, this class accepts a generic type for ApplicationEventPublisher to select EventListener when publishing events.

  • ApplicationEventPublisher: Encapsulates the method of publishing events and notifies all listeners of the event registered in Spring for processing.

  • ApplicationEventPublisherAware: One of the Aware interfaces provided by Spring, the Bean that implements this interface can obtain ApplicationEventPublisher and publish events.

Use Aware

An example of event publishing-subscribing using the Spring event mechanism:

import org.springframework.context.ApplicationEvent;

/**
 * @Author: chenyang
 * @DateTime: 2022/9/21 11:07
 * @Description: 事件对象
 */
public class SpringEventAware extends ApplicationEvent {

    private String msg;

    public SpringEventAware(Object source, String msg) {
        super(source);
        this.msg = msg;
    }

    public SpringEventAware(Object source) {
        super(source);
    }

    public String getMsg() {
        return msg;
    }
}
import com.yang.common.event.SpringEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @Author: chenyang
 * @DateTime: 2022/9/21 11:08
 * @Description: 事件监听者,事件监听者实现ApplicationListener<E extends ApplicationEvent>, 交由 Spring 进行管理,无需自己进行监听器的注册与通知过程
 */
@Component
public class SpringListenerAware implements ApplicationListener<SpringEventAware> {
    @Override
    public void onApplicationEvent(SpringEventAware event) {
        System.out.println("publish event, msg is : " + event.getMsg());
    }
}
import com.yang.common.event.SpringEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

/**
 * @Author: chenyang
 * @DateTime: 2022/9/21 11:09
 * @Description: 事件源
 */
@Component
public class SpringPublishAware implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    public void publishEvent(String msg){
        applicationEventPublisher.publishEvent(new SpringEventAware(this, msg));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}
@Autowired
private SpringPublishAware springPublishAware;

@Test
void contextLoads2() {
    springPublishAware.publishEvent("通过 Spring 实现发布订阅");
}

Not using Aware

@Data
public class Task {
    private String name;
    private String address;
}


public class SpringEvent extends ApplicationEvent {
    private Task task;

    public SpringEvent(Task task) {
        super(task);
        this.task = task;
    }

    public Task getTask() {
        return task;
    }
}


@Component
public class SpringListener implements ApplicationListener<SpringEvent> {
    @Override
    public void onApplicationEvent(SpringEvent event) {
        Task task = event.getTask();
        System.err.println("事件接受任务");
        System.err.println(task);
        System.err.println("任务完成");
    }
}
@Autowired
private ApplicationEventPublisher publisher;

@Test
void contextLoads3() {
    Task task = new Task();
    task.setName("admin");
    task.setAddress("unknown area");
    SpringEvent event = new SpringEvent(task);
    System.out.println("开始发布任务");
    publisher.publishEvent(event);
    System.out.println("发布任务完成");
}

        You can see it in the above code. There is no difference between using events in the Spring framework and using the time mechanism in Java. They are composed of event sources, event objects, and event listeners. Different from the event mechanism provided by Java natively, Spring provides ApplicationEventthe class as the base class, on which developers can define their own custom events.

        In Spring, listeners of event objects inherited from ApplicationEvent can be managed by the Spring container and published through ApplicationEventPublisher when publishing. This avoids our own implementation of the listener registration and notification process, eliminating a lot of complicated processes and making us more focused on the business itself.

Spring event publishing process and source code analysis

ApplicationEvent

/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public abstract class ApplicationEvent extends EventObject {

    /** use serialVersionUID from Spring 1.2 for interoperability */
    private static final long serialVersionUID = 7099057708183571937L;

    /** System time when the event happened */
    private final long timestamp;


    /**
     * Create a new ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }


    /**
     * Return the system time in milliseconds when the event happened.
     */
    public final long getTimestamp() {
        return this.timestamp;
    }
}

        ApplicationEvent inherits the event object EventObject in JDK, and all event objects in Spring should inherit from ApplicationEvent. On the basis of Spring, it adds the timestamp attribute and serialized ID when the event occurs, and provides a construction method through the event source. The ApplicationEvent in Spring is set as an abstract class. Since a single ApplicationEvent has no semantics, it needs to be extended according to different scenarios to give meaning to the event. In this type of description, the author also explained this point very well.

ApplicationListener

The EventListener interface is provided in JDK as an event listener mark. Spring provides the ApplicationListener interface on the basis of the EventListener interface. This interface receives a subclass of ApplicationEvent to complete the event monitoring process. The specific source code is as follows:

/**
 * Interface to be implemented by application event listeners.
 * Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
 * that it is interested in. When registered with a Spring ApplicationContext, events
 * will be filtered accordingly, with the listener getting invoked for matching event
 * objects only.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @param <E> the specific ApplicationEvent subclass to listen to
 * @see org.springframework.context.event.ApplicationEventMulticaster
 */
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);

}

This interface is a functional interface that provides a onApplicationEvent(E extends Application)method definition. All self-implemented listeners need to implement this interface and process events in this method.

listener registration

In Spring, we don't need to manually register the listener. Once the ApplicationListener object is registered in the Spring container, Spring will register the listener to implement event monitoring.

Before introducing the listener registration process, we first need to introduce it org.springframework.context.event.ApplicationEventMulticaster. It mainly defines the management event listener and the related operations of publishing events to the listener. If there is no definition, the Spring container will be instantiated by default SimpleApplicationEventMulticaster.

In Spring, when the container is initialized org.springframework.context.ConfigurableApplicationContext, reFresh()the method in the interface will be called to load the Bean, and this method will perform event monitoring registration. The specific code is as follows:

The code for listener registration is located org.springframework.context.supportAbstractApplicationContextin refresh()the method of the class, as follows:

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);
         beanPostProcess.end();

         // Initialize message source for this context.
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
         contextRefresh.end();
      }
   }
}

In the code for the refresh() method, notice the following two items:

  • Call initApplicationEventMulticaster()the method to initialize an ApplicationEventMulticaster, which is initialized to SimpleApplicationEventMulticaster by default.

  • Call registerListeners()the method to register the event listener. The specific implementation of this method is as follows:

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 {
      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() + "]");
      }
   }
}
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);
      }
   }
}

As can be seen from the above code, the process of registering listeners can be divided into the following three parts:

  • Add the listener specified in the container, usually this part of the added listener is controlled by Spring;

  • BeanFactoryObtain all BeanNames that implement the ApplicationListener interface from and push them to ApplicationEventMulticaster

  • If there are events that need to be executed immediately, directly execute the release of these events

The above three steps are the logic related to event listener registration performed by Spring when initializing Beans. During the bean loading process, the event listener registration is completed, and we don't need to register the listener for the custom event by ourselves.

ApplicationEventPublisher

In Spring, the process of publishing a custom event can be summarized by the following line of code:

applicationEventPublisher.publishEvent(new SpringEvent(msg));
// applicationEventPublisher.publishEvent(new SpringEvent(this, msg));

Among them, applicationEventPublisheris ApplicationEventPublisherthe instance . Through the above code in the event source, a custom event can be published in Spring. publishEventAfter posting the task using its method, the code enters org.springframework.context.support.AbstractApplicationContextthe logic.

AbstractApplicationContext

The entire event publishing logic is in this class and its subclasses, and the final method of publishing events is publishEvent(Object event, @Nullable ResolvableType eventType).

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();
      }
   }

   // 在早期事件,容器初始化时候使用,可以忽略
   if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
   }
   else {
       // 进行任务广播的主要逻辑
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
   }

   // 方便使用父类进行发布事件,非重点
   if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
         ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
      }
      else {
         this.parent.publishEvent(event);
      }
   }
}

Specifically, it can be divided into the following three steps:

  1. Split the event ApplicationEventinto PayloadApplicationEventtwo parts and . Among them, the events we customize in Spring are all ApplicationEventof type , PayloadApplicationEventusually the events of the Spring framework itself;

  2. multicasterIf it has not been loaded, store it EarlyApplicationEventsin the queue and multicasterrelease it immediately after the initialization is successful;

  3. Also publish the event to the ApplicationContextparent

The above three steps are the process of publishing an event. Since the custom events we publish are usually after the container is loaded, and the custom events are all ApplicationEvent processes, so usually only getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType)this sections:

  1. getApplicationEventMulticaster(): get the container ApplicationEventMulticaster. This content is mainly a tool class used by Spring to assist publishing tasks

  2. ApplicationEventMulticaster.multicastEvent(applicationEvent, eventType): The content that actually publishes the event.

ApplicationEventMulticaster

It is org.springframework.context.event.AbstractApplicationEventMulticasteran implementation class, mainly for publishing auxiliary events, and the core logic of its internal publishing tasks is in multicastEventit.

@Override
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);
      }
   }
}

Here we take the method definition in SimpleApplicationEventMulticaster as an example, as the type of default injection, and usually our default event publishing process follows this implementation. As can be seen from the program, the main logic of multicastEvent can be divided into three parts:

  1. Get the event type, mainly used to get the actual type of Spring Event.resolveDefaultEventType(event))

  2. getApplicationListeners(event, type)Obtain the listener for this event and event type according to the event and event type

  3. Traverse the set of listeners, and notify through the Executor held in the multicaster, and finally call onApplicationEventthe method , which is the method we must override when customizing ApplicationListener.

AbstractApplicationEventMulticaster

The main logic of getting the listener is org.springframework.context.event.AbstractApplicationEventMulticasteringetApplicationListeners(event, type)

protected Collection<ApplicationListener<?>> getApplicationListeners(
      ApplicationEvent event, ResolvableType eventType) {

   Object source = event.getSource();
   Class<?> sourceType = (source != null ? source.getClass() : null);
   ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

   // Potential new retriever to populate
   CachedListenerRetriever newRetriever = null;

   // Quick check for existing entry on ConcurrentHashMap
   CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
   if (existingRetriever == null) {
      // Caching a new ListenerRetriever if possible
      if (this.beanClassLoader == null ||
            (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                  (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
         newRetriever = new CachedListenerRetriever();
         existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
         if (existingRetriever != null) {
            newRetriever = null;  // no need to populate it in retrieveApplicationListeners
         }
      }
   }

   if (existingRetriever != null) {
      Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
      if (result != null) {
         return result;
      }
      // If result is null, the existing retriever is not fully populated yet by another thread.
      // Proceed like caching wasn't possible for this current local attempt.
   }

   return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

The general process: Construct a cache key through the time type and the data source type in the event, and first go to the cache to obtain whether there is an event handler corresponding to the key. If it does not exist, build a new one ListenerRetriever, and then call retrieveApplicationListenersthe method to get the listening listener.

retrieveApplicationListeners

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
      ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

   List<ApplicationListener<?>> allListeners = new ArrayList<>();
   Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
   Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

   Set<ApplicationListener<?>> listeners;
   Set<String> listenerBeans;
   synchronized (this.defaultRetriever) {
      listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
      listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
   }

   // Add programmatically registered listeners, including ones coming
   // from ApplicationListenerDetector (singleton beans and inner beans).
   for (ApplicationListener<?> listener : listeners) {
      if (supportsEvent(listener, eventType, sourceType)) {
         if (retriever != null) {
            filteredListeners.add(listener);
         }
         allListeners.add(listener);
      }
   }

   // Add listeners by bean name, potentially overlapping with programmatically
   // registered listeners above - but here potentially with additional metadata.
   if (!listenerBeans.isEmpty()) {
      ConfigurableBeanFactory beanFactory = getBeanFactory();
      for (String listenerBeanName : listenerBeans) {
         try {
            if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
               ApplicationListener<?> listener =
                     beanFactory.getBean(listenerBeanName, ApplicationListener.class);
               if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                  if (retriever != null) {
                     if (beanFactory.isSingleton(listenerBeanName)) {
                        filteredListeners.add(listener);
                     }
                     else {
                        filteredListenerBeans.add(listenerBeanName);
                     }
                  }
                  allListeners.add(listener);
               }
            }
            else {
               // Remove non-matching listeners that originally came from
               // ApplicationListenerDetector, possibly ruled out by additional
               // BeanDefinition metadata (e.g. factory method generics) above.
               Object listener = beanFactory.getSingleton(listenerBeanName);
               if (retriever != null) {
                  filteredListeners.remove(listener);
               }
               allListeners.remove(listener);
            }
         }
         catch (NoSuchBeanDefinitionException ex) {
            // Singleton listener instance (without backing bean definition) disappeared -
            // probably in the middle of the destruction phase
         }
      }
   }

   AnnotationAwareOrderComparator.sort(allListeners);
   if (retriever != null) {
      if (filteredListenerBeans.isEmpty()) {
         retriever.applicationListeners = new LinkedHashSet<>(allListeners);
         retriever.applicationListenerBeans = filteredListenerBeans;
      }
      else {
         retriever.applicationListeners = filteredListeners;
         retriever.applicationListenerBeans = filteredListenerBeans;
      }
   }
   return allListeners;
}

The main logic in this method is very simple. Listening matching is mainly performed by looping Listeners. The source of Listeners is mainly divided into two parts:

listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);

One part is the listener that already exists in the container, and the other part is the name of the string identifier of the listener bean. applicationListenerBeans is mainly to deal with beans that have not been added to the listener collection after they are declared.

The process of event publishing

combat

public class UserEvent {

    private String username;

    private String password;
}
@Component
public class UserListener implements ApplicationListener<PayloadApplicationEvent<UserEvent>> {

    @Async
    @Override
    public void onApplicationEvent(PayloadApplicationEvent<UserEvent> event) {
        UserEvent payload = event.getPayload();
        System.out.println("result: " + payload);
    }
}
public interface DomainEventPublisher {

    void publish(Object object);
}
@ConditionalOnMissingBean(DomainEventPublisher.class)
@Configuration
public class EventPublisherConfig{
    
    private final ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    public EventPublisherConfig(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Bean
    public DomainEventPublisher domainEventPublisher(){
        return event -> {
            System.out.println("事件发布:" + event);
            applicationEventPublisher.publishEvent(event);
        };
    }

}
    @Autowired
    private DomainEventPublisher domainEventPublisher;

    @Test
    void contextLoads() {
        UserEvent userEvent = new UserEvent();
        userEvent.setUsername("username");
        userEvent.setPassword("123");
        System.out.println("事件开始");
        domainEventPublisher.publish(userEvent);

        System.out.println("事件结束");
    }

Guess you like

Origin blog.csdn.net/weixin_46058921/article/details/126978976