11. Event monitoring in Spring

Event Listening for Spring

ApplicationListener

The ApplicationListener is part of the Spring event mechanism and works with the abstract class ApplicationEvent to complete the event mechanism for the ApplicationContext.

If an ApplicationListener's Bean exists in the container, the corresponding Bean is triggered when the ApplicationContext calls the publishEvent method.This process is the implementation of a typical observer pattern.

Source code:

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

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

}

Listening for ContextRefreshedEvent events

Take Spring's built-in event ContextRefreshedEvent for example. When an ApplicationContext is initialized or refreshed, the ContextRefreshedEvent event is triggered. Here we implement an ApplicationListener to listen for this event.

@Component // The class needs to be instantiated as a Bean
public class LearnListener implements ApplicationListener<ContextRefreshedEvent> {
   @Override
   public void onApplicationEvent(ContextRefreshedEvent event) {
      // Number of Bean s in Print Container
      System.out.println("Listener Gets Initialization in Container Bean Number:" + event.getApplicationContext().getBeanDefinitionCount());
   }
}

Event Publishing

After the container is created, an event is published in the finishRefresh() method - ContextRefreshedEvent

Let's take a closer look at how this event was published

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
        }
    }

    // Multicast right now if possible - or lazily once the multicaster is initialized
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    }
    else {
        //Get the dispatcher for the event
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    }

    // Publish event via parent context as well...
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
        }
        else {
            this.parent.publishEvent(event);
        }
    }
}

Dispatch event: getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

Executing invokeListener here is primarily an interface method that calls listener back and forth

This is the process of event publishing in spring.

Event Dispatcher

One of the steps in event publishing is to get the event dispatcher. Where was the event dispatcher created?

In fact, when the container is initialized, the initApplicationEventMulticaster() method is executed to initialize the event dispatcher for the container.

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    //First determine if there is an applicationEventMulticaster in the container
    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 {
        //Create a dispatcher if not
        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() + "]");
        }
    }
}

APPLICATION_EVENT_MULTICASTER_BEAN_NAME:

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

Where does the listener come from

The refresh() method executes registerListeners() to register listeners in the container

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!
    //Get Bean Names for All Listeners by Type
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
       //Add listener to dispatcher
       getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

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

@EventListener

In addition to implementing the ApplicationListener interface to complete event listening, the @EventListener annotation also listens for events

Simply label @EventListener as a method:

@EventListener(classes = {ApplicationEvent.class})
public void listen(ApplicationEvent applicationEvent){
    System.out.println("Monitor:"+applicationEvent);
}

Keywords: Java Spring

Added by rtpmatt on Sun, 10 Nov 2019 05:40:38 +0200