Spring event mechanism

concept

In a complete event system, there are the following roles

  1. Event: describe what happened, such as request processing completed and Spring container refresh completed
  2. Event source: the generator of an event. Any event must have an event source. For example, the event source of request processing is DispatcherServlet, and the event source of Spring container refresh is ApplicationContext
  3. Event broadcaster: a bridge between events and event listeners, responsible for notifying events to event listeners
  4. Event listener: monitors the occurrence of events and can do some processing in the listener

Spring events

Our common event may be ApplicationContextEvent. Its subclass ContextRefreshedEvent is our common event type and is published after Spring instantiates all non deferred loaded bean s.

Let's look at the architecture of Spring events

Spring listener

Event broadcaster

ApplicationContext support for events

ApplicationEventPublisher is an event publisher provided by Spring for users. The real publishing function is entrusted to the above applicationeventmulticast.

Spring provides application event publisher aware, which allows users to obtain the publisher for event publishing.

Mode of use

Spring provides two ways

  1. Implement the ApplicationListener interface
  2. Use annotation @ EventListener

Annotation implementation source code

We can directly see EventListenerMethodProcessor. This class implements the smartinitializingsingsingleton interface, which will be called after Spring initializes all non deferred loaded bean s.

public interface SmartInitializingSingleton {

   /**
    * Invoked right at the end of the singleton pre-instantiation phase,
    * with a guarantee that all regular singleton beans have been created
    * already. {@link ListableBeanFactory#getBeansOfType} calls within
    * this method won't trigger accidental side effects during bootstrap.
    * <p><b>NOTE:</b> This callback won't be triggered for singleton beans
    * lazily initialized on demand after {@link BeanFactory} bootstrap,
    * and not for any other bean scope either. Carefully use it for beans
    * with the intended bootstrap semantics only.
    */
   void afterSingletonsInstantiated();
}

In fact, the implementation logic is very simple

for (Method method : annotatedMethods.keySet()) {
   for (EventListenerFactory factory : factories) {
      if (factory.supportsMethod(method)) {
         Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
         ApplicationListener<?> applicationListener =
               factory.createApplicationListener(beanName, targetType, methodToUse);
         if (applicationListener instanceof ApplicationListenerMethodAdapter) {
            ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
         }
         context.addApplicationListener(applicationListener);
         break;
      }
   }
}

Find out all the methods modified by annotations, create a corresponding ApplicationListener, and call the method after receiving the event.

public void processEvent(ApplicationEvent event) {
   Object[] args = resolveArguments(event);
   if (shouldHandle(event, args)) {
      Object result = doInvoke(args);
      if (result != null) {
         handleResult(result);
      }
      else {
         logger.trace("No result object given - no result to handle");
      }
   }
}

The sequence of listener calls

We can see in abstractapplicationeventmulticast #retrieveapplicationlisteners that it supports us to specify the order of listeners, and many Spring related orders can be used

  1. Implement the Ordered interface
  2. Implement the PriorityOrdered interface
  3. Using the @ Ordered interface

Asynchronous call listener

By default, the event broadcaster created by Spring calls the notification listener synchronously. We can set or replace the Spring default listener to achieve the purpose of asynchronous call. Of course, it can also be extended. It adopts synchronous or asynchronous methods according to different events, rather than a single or all synchronous or all asynchronous

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

Initialize the event propagator before Spring refresh

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

Replace the original event propagator

@Component("applicationEventMulticaster")
public class TestEventMulticaster extends SimpleApplicationEventMulticaster {
}

Keywords: Java Spring Spring Boot

Added by bigdaddykraven on Sat, 15 Jan 2022 11:17:38 +0200