编程知识 cdmana.com

Spring notes (7) - spring's event and listening mechanism

One . background

   Event mechanism as a programming mechanism , Support is provided in many development languages , At the same time, many open source frameworks are designed with event mechanism , such as SpringFramework.

   stay Java In language ,Java Participants in the event mechanism are 3 Roles :

    1.Event Source: The specific source of the event , for instance , You click on a... In the interface button Button , So this button is the event source , To make a button respond to certain events , You need to register a specific listener listener, The event source passes the event object to all registered listeners ;

    2.Event Object: Event state object , In the corresponding method for the listener , As a parameter ;

    3.Event Listener: Event listener , When it listens event object When it came into being , It calls the corresponding method to process ;

     above 3 One role defines the basic model of the event mechanism .

     The process is :

      1) First, we register the listener object with the event source object , In this way, when the event is triggered, the system can access the corresponding listener through the event source ;

      2) When the event source triggers an event , The system encapsulates the relevant information of events into event objects of corresponding types , And send it to the appropriate listener registered with the event source ;

      3) When the event object is sent to the listener , The system calls the corresponding event handling method of the listener to handle the event , That is, to respond ;

  PS: Between the listener and the event source is “ Many to many ” The relationship between .

   The following is a detailed introduction to several concepts :

   1) Event source : Events are initially generated by the event source , The source of the event can be Java Bean Or by an object capable of generating events . stay Java in , What kind of events will each component produce , It's been defined . Or say , For any event , Which components can generate it , It's certain .

   2) Event object : The highest level in the event object is java.util.EventObject, All event state objects are derived from this class , such as Spring-context The root class of the event mechanism of is ApplicationEvent, and Apollo Event handling class of configuration center AppCreationEvent It is inherited from Spring Of ;

     except EventObject Is in util In bag , Everything else is java.awt、java.awt.event Package or java.swing、java.swing.event In bag , Such as the AWTEvent、ActionEvent、AdjustmentEvent wait ;

    • EventObject: This class except from Object In addition to the inherited methods in the class getSource Method , Its function is to return to the original occurrence Event The object of ;
      public class EventObject implements java.io.Serializable {
      
          protected transient Object  source;
      
          public EventObject(Object source) {
              if (source == null)
                  throw new IllegalArgumentException("null source");
      
              this.source = source;
          }
      
          public Object getSource() {
              return source;
          }
      
          public String toString() {
              return getClass().getName() + "[source=" + source + "]";
          }
      }
    • ApplicationEvent:
      public abstract class ApplicationEvent extends EventObject {
          private static final long serialVersionUID = 7099057708183571937L;
          private final long timestamp = System.currentTimeMillis();
      
          public ApplicationEvent(Object source) {
              super(source);
          }
      
          public final long getTimestamp() {
              return this.timestamp;
          }
      }
    • AppCreationEvent:
      public class AppCreationEvent extends ApplicationEvent {
      
        public AppCreationEvent(Object source) {
          super(source);
        }
      
        public App getApp() {
          Preconditions.checkState(source != null);
          return (App) this.source;
        }
      }

   3)  Listener object : A listener object is an instance of a class that implements a specific listener interface , At the top of the listener interface is  java.util.EventListener, This interface is the markup interface that all event listener interfaces must extend . To my surprise, this interface is completely empty , There's no definition of any abstract method ;

public interface EventListener {
}

   The interface of event listener is named as :XXListener, and , stay java in , These interfaces have been defined . To be implemented , It defines the event handler ( This is the prototype of event handling , This method needs to be re implemented ). for example :ActionListener Interface 、MouseListener Interface 、WindowListener Interfaces, etc. ;

   Said so much , Now let's get back to business , Speaking of Spring Event mechanism , It's from Spring The container began to talk about , stay IOC The starting process of the container , When all bean All of them have been initialized. After the processing is finished ,Spring IOC The container will have an action to publish the event .

public void refresh() throws BeansException, IllegalStateException {
        // Let's have a lock , Otherwise  refresh()  Isn't over , You have another operation to start or destroy the container , That's a mess 
        synchronized (this.startupShutdownMonitor) {
                
                ..............
                // Initialize the container's information source 
                initMessageSource();

                // Initialize event listening multiplexer 
                initApplicationEventMulticaster();

                // It's an empty shell method , stay AnnotationApplicationContex There is no implementation context in , May be in spring Later versions will expand .
                onRefresh();

                // Register listener 
                registerListeners();

                
                // Object creation : Initialize all the remaining ( Non lazy loaded ) Single instance objects 
                finishBeanFactoryInitialization(beanFactory);

                // Refresh to finish the job , Include initialization LifecycleProcessor, Publish refresh completion events, etc 
                finishRefresh();
        }
            .................
}

    stay  AbstractApplicationContext Class finishRefresh Method , It will post ( radio broadcast ) A message indicating the end of initialization :

    protected void finishRefresh() {
        // Clear context-level resource caches (such as ASM metadata from scanning).
        clearResourceCaches();

        // Initialize lifecycle processor for this context.
        initLifecycleProcessor();

        // Propagate refresh to lifecycle processor first.
        getLifecycleProcessor().onRefresh();

        // Release ( radio broadcast ) A message , type ContextRefreshedEvent representative Spring End of container initialization 
        publishEvent(new ContextRefreshedEvent(this));

        // Participate in LiveBeansView MBean, if active.
        LiveBeansView.registerApplicationContext(this);
    }

    such , When Spring IOC The container is loaded and processed bean after , It also gives us an opportunity , You can do what you want to do . This is also Spring IOC The container provides a place for external extensions , We can use this extension mechanism , To achieve some special business needs .

   Let's take our bean Realization   ApplicationListener Interface , This way, when the event is published , Spring Of IOC The container takes the instance object in the container as the event source class , And find the listener of the event , At this point, the corresponding method is executed, such as onApplicationEvent(E event), Our business logic code will be written in this method . In this way, our goal can be achieved , But you may have a question , Such code can be implemented by InitializingBean Interface , Will also be Spring Container calls automatically . But if our needs are what we need to do , It's necessary to wait until all the bean After they've been processed , here InitializingBean The interface is appropriate .( You can see the case below 3)

   Spring The event mechanism is the implementation of observer design pattern , adopt ApplicationEvent and ApplicationListener Interface , Can implement container event handling .

  ApplicationListener monitor ApplicationEvent And its subclasses :

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    void onApplicationEvent(E event);

}

   If there is a listener in the container , Whenever the container publishes an event , The monitor will be triggered automatically , Of course, this event mechanism must be triggered by program display .

   among Spring There are some built-in Events , Some event actions are issued when an operation is completed . For example, in the above monitoring ContextRefreshedEvent event , When all bean This event will be triggered after both initialization and successful loading , Realization ApplicationListener<ContextRefreshedEvent> The interface can receive monitoring actions , How to implement your own business logic .

   Here are some Spring Built in events :

    • ContextRefreshedEvent:ApplicationContext  When initialized or refreshed , The incident was released . It can also be found in  ConfigurableApplicationContext Used in the interface refresh() Method to happen . Initialization here means : be-all Bean Successfully loaded , Post Processors Bean Detected and activated , all Singleton Bean Pre instantiated ,ApplicationContext The container is ready for use ;【 Container refresh completes all bean The event will be published when it is fully created 】
    • ContextStartedEvent: When using  ConfigurableApplicationContext (ApplicationContext A subinterface ) Interface start() Method start up  ApplicationContext  when , The incident was released . You can investigate your database , Or you can restart any stopped application after receiving this event ;
    • ContextStoppedEvent: When using  ConfigurableApplicationContext  Interface stop() stop it  ApplicationContext  when , Publish this event . You can do the necessary cleanup work after receiving this incident ;
    • ContextClosedEvent: When using  ConfigurableApplicationContext  Interface close() Method shut down  ApplicationContext  when , The incident was released . A closed context reaches the end of the lifecycle , It can't be refreshed or restarted ;【 Closing the container will publish this event 】

    • RequestHandledEvent: This is a web-specific event , Tell all bean HTTP The request has been served . It can only be used for DispatcherServlet Of Web application . In the use of Spring As the front end MVC Controller , When Spring After processing the user request , The system will automatically trigger the event ;

  Two . Case study

   1. Simple implementation of custom listener , Get to know the monitor : combination Last article Case study , Implement listener monitoring Spring Changes in the container

@Configuration
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    // When the container publishes this event , Methods the trigger 
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println(" Current events received :"+event);
    }
}
========= Test run results =========
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean The number of :11
 November  03, 2020 10:17:59  Afternoon  org.springframework.context.annotation.ConfigurationClassPostProcessor enhanceConfigurationClasses
 Information : Cannot enhance @Configuration bean definition 'myBeanDefinitionRegistryPostProcessor' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanFactory--->bean The number of :12
[MyBeanFactoryPostProcessor] Called postProcessBeanFactory
[MyBeanFactoryPostProcessor] At present beanFactory share 12 individual bean
[MyBeanFactoryPostProcessor] At present beanFactory There are the following components [org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, extConfig, myApplicationListener, myBeanDefinitionRegistryPostProcessor, myBeanFactoryPostProcessor, myBeanPostProcessor, person, color]
PropertyValues: length=0
[MyBeanFactoryPostProcessor]postProcessBeanFactory Method has been modified name The initial value of the attribute is 
PropertyValues: length=1; bean property 'name'
[MyBeanPostProcessor] Post processor processing bean=【extConfig】 Start 
[MyBeanPostProcessor] Post processor processing bean=【extConfig】 complete !
[MyBeanPostProcessor] Post processor processing bean=【myApplicationListener】 Start 
[MyBeanPostProcessor] Post processor processing bean=【myApplicationListener】 complete !
Person There are reference constructors :[name= Zhang San ,sex= male ]
[Person] Called BeanNameAware Of setBeanName The method :person
[Person] Called BeanFactoryAware Of setBeanFactory The method :org.springframework.beans.factory.support.DefaultListableBeanFactory@e45f292: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,extConfig,myApplicationListener,myBeanDefinitionRegistryPostProcessor,myBeanFactoryPostProcessor,myBeanPostProcessor,person,color]; root of factory hierarchy
[MyBeanPostProcessor] Post processor processing bean=【person】 Start 
[Person] Called Initailization Of afterPropertiesSet The method 
[MyBeanPostProcessor] Post processor processing bean=【person】 complete !
[MyBeanPostProcessor] Post processor processing bean=【color】 Start 
[MyBeanPostProcessor] Post processor processing bean=【color】 complete !
 Current events received :org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:17:59 CST 2020]
Person [name= Zhao si , sex=null]
 Current events received :org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:17:59 CST 2020]
[Person] Called DisposableBean Of destroy The method 

   2. Custom post an event , Steps are as follows :

    1) Implement a listener to listen for an event ( The event is the realization of ApplicationEvent And its subclasses );

    2) Add the listener to the container ;

    2) Release events , As long as the container has a release of related events , We can monitor this event ;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExtConfig.class);
        Person bean = context.getBean(Person.class);
        System.out.println(bean.toString());
        // Release events 
        context.publishEvent(new ApplicationEvent(new String(" Custom events ")) {
            });
        context.close();
    }
========= Test run results =========
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean The number of :11
...............
[MyBeanPostProcessor] Post processor processing bean=【color】 complete !
 Current events received :org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:40:39 CST 2020]
Person [name= Zhao si , sex=null]
 Current events received :com.hrh.ext.ExtTest$1[source= Custom events ]
 Current events received :org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e817b38, started on Tue Nov 03 22:40:39 CST 2020]
[Person] Called DisposableBean Of destroy The method 

     From the above running results, we can see that , We added an event to the container , When we post events , The listener will listen to the event and publish the content of the event .

    3. Custom listener , Implement the container bean After initialization, perform the corresponding operation , For example, executing specific methods :

    1) Custom annotation :

// The annotation acts on the class 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BaseService {
}

    2) Two tests Mapper:

@Configuration
@BaseService
public class TaskScheduleJobMapper {
    public void initMapper() {
        System.out.println(">>>>> 【initMapper】Start job <<<<<");
    }
}


@Configuration
@BaseService
public class TaskScheduleJobTxlogMapper {
    public void initMapper() {
        System.out.println(">>>>> 【initMapper】Recording log <<<<<");
    }
}

    3) Test system entry :

public interface BaseInterface {
    public void init();
}

@Configuration
public class BaseInterfaceImpl implements BaseInterface {
    @Override
    public void init() {
        System.out.println("System start........");
    }
}    

    4) Custom listener :

@Configuration
public class ApplicationContextListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        // root application context
        if (null == contextRefreshedEvent.getApplicationContext().getParent()) {
            System.out.println(">>>>> Spring Initialization done  <<<<<");
            // spring After initialization , Call all uses through reflection BaseService Annotated initMapper Method 
            Map<String, Object> baseServices =
                    contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(BaseService.class);
            for (Object service : baseServices.values()) {
                System.out.println(">>>>> {" + service.getClass().getName() + "}.initMapper():");
                try {
                    Method initMapper = service.getClass().getMethod("initMapper");
                    initMapper.invoke(service);
                } catch (Exception e) {
                    System.out.println(" initialization BaseService Of initMapper The method is abnormal :" + e);
                    e.printStackTrace();
                }
            }
            //  System entry initialization 
            Map<String, BaseInterface> baseInterfaceBeans =
                    contextRefreshedEvent.getApplicationContext().getBeansOfType(BaseInterface.class);
            for (Object service : baseInterfaceBeans.values()) {
                System.out.println(">>>>> {" + service.getClass().getName() + "}.init()");
                try {
                    Method init = service.getClass().getMethod("init");
                    init.invoke(service);
                } catch (Exception e) {
                    System.out.println(" initialization BaseInterface Of init The method is abnormal :" + e);
                    e.printStackTrace();
                }
            }
        }
    }
}


======= Test run results =======
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean The number of :14
................
[MyBeanPostProcessor] Post processor processing bean=【color】 Start 
[MyBeanPostProcessor] Post processor processing bean=【color】 complete !
>>>>> Spring Initialization done  <<<<<
>>>>> {com.hrh.ext.TaskScheduleJobMapper$$EnhancerBySpringCGLIB$$6bfe7114}.initMapper():
>>>>> 【initMapper】Start job <<<<<
>>>>> {com.hrh.ext.TaskScheduleJobTxlogMapper$$EnhancerBySpringCGLIB$$7132ffe6}.initMapper():
>>>>> 【initMapper】Recording log <<<<<
>>>>> {com.hrh.ext.BaseInterfaceImpl$$EnhancerBySpringCGLIB$$f49a26ba}.init()
System start........
Person [name= Zhao si , sex=null]
[Person] Called DisposableBean Of destroy The method 

  4. Custom events and monitoring and publishing

    1) Custom events :

public class EmailEvent extends ApplicationEvent {
    private String address;
    private String text;

    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public EmailEvent(Object source) {
        super(source);
    }

    public EmailEvent(Object source, String address, String text) {
        super(source);
        this.address = address;
        this.text = text;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}

    2) Custom listeners listen for custom events :

@Component
public class EmailListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof EmailEvent) {

            EmailEvent emailEvent = (EmailEvent) event;
            System.out.println(" mailing address :" + emailEvent.getAddress());
            System.out.println(" Email content :" + emailEvent.getText());
        } else {
            System.out.println(" The container itself event :" + event);
        }

    }
}

     3) Test release events :

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExtConfig.class);

        Person bean = context.getBean(Person.class);
        System.out.println(bean.toString());
        // Create a ApplicationEvent object 
        EmailEvent event = new EmailEvent("hello","249968839@qq.com","This is a event test");
        // Actively trigger the event 
        context.publishEvent(event);
        context.close();
    }
======= Test run results =======
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean The number of :15
.............
>>>>> Spring Initialization done  <<<<<
>>>>> {com.hrh.ext.TaskScheduleJobMapper$$EnhancerBySpringCGLIB$$45955a2d}.initMapper():
>>>>> 【initMapper】Start job <<<<<
>>>>> {com.hrh.ext.TaskScheduleJobTxlogMapper$$EnhancerBySpringCGLIB$$4ac9e8ff}.initMapper():
>>>>> 【initMapper】Recording log <<<<<
>>>>> {com.hrh.ext.BaseInterfaceImpl$$EnhancerBySpringCGLIB$$e5b13f13}.init()
System start........
 The container itself event :org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Fri Nov 06 22:55:23 CST 2020]
Person [name= Zhao si , sex=null]
 The container itself event :com.hrh.ext.ExtTest$1[source= Custom events ]
 mailing address :249968839@qq.com
 Email content :This is a event test
 The container itself event :org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Fri Nov 06 22:55:23 CST 2020]
[Person] Called DisposableBean Of destroy The method 

3、 ... and . principle

   There are several cases , Now you should know how to use it , Next through debug Code to analyze its execution process .

  1. First look at it.  ContextRefreshedEvent How the event was released , Or from the familiar formula refresh() Speak up , When the container is refreshed , You can see that it calls  finishRefresh() To refresh the execution event :

public void refresh() throws BeansException, IllegalStateException {
        // Let's have a lock , Otherwise  refresh()  Isn't over , You have another operation to start or destroy the container , That's a mess 
        synchronized (this.startupShutdownMonitor) {
                
                ..............
                // Initialize the container's information source 
                initMessageSource();

                // Initialize event listening multiplexer 
                initApplicationEventMulticaster();

                // It's an empty shell method , stay AnnotationApplicationContex There is no implementation context in , May be in spring Later versions will expand .
                onRefresh();

                // Register listener 
                registerListeners();

                
                // Object creation : Initialize all the remaining ( Non lazy loaded ) Single instance objects 
                finishBeanFactoryInitialization(beanFactory);

                // Refresh to finish the job , Include initialization LifecycleProcessor, Publish refresh completion events, etc 
                finishRefresh();
        }
            .................
}

  2. stay AbstractApplicationContext.finishRefresh Methods will publish ( radio broadcast ) A message indicating the end of initialization :publishEvent(new ContextRefreshedEvent(this))

    protected void finishRefresh() {
        // Clear context-level resource caches (such as ASM metadata from scanning).
        clearResourceCaches();

        // Initialize lifecycle processor for this context.
        initLifecycleProcessor();

        // Propagate refresh to lifecycle processor first.
        getLifecycleProcessor().onRefresh();

        // Release ( radio broadcast ) A message , type ContextRefreshedEvent representative Spring End of container initialization 
        publishEvent(new ContextRefreshedEvent(this));

        // Participate in LiveBeansView MBean, if active.
        LiveBeansView.registerApplicationContext(this);
    }

  3. continue  publishEvent Methods will find that they are executed getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType) To broadcast the news :

    public void publishEvent(ApplicationEvent event) {
        publishEvent(event, null);
    }
    
    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;// Type conversion 
        }
        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
        // During initialization registerListeners Method will bring earlyApplicationEvents Set to null ,( Early events , The container is initialized with , You can ignore )
        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        }
        else {
            // Executing broadcast messages 
            getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        }

        // Publish event via parent context as well...  It is convenient to use the parent class to publish events 
        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }
    

    PS:publishEvent The method is to publish ( radio broadcast ) The core competence of service , And it's defined in  ApplicationEventPublisher Interface ,ApplicationContext Interface inherited  ApplicationEventPublisher, therefore  AbstractApplicationContext abstract class (ApplicationContext Implementation class of interface ) This method is realized , It also has the ability to send broadcasts .

     above  getApplicationEventMulticaster() What is it ? We need to know more about , Its role is to get the event multicast ( dispatcher ), Sending events to multiple listeners , Let the listener execute the corresponding logic .

    private ApplicationEventMulticaster applicationEventMulticaster;
    ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
        if (this.applicationEventMulticaster == null) {
            throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
                    "call 'refresh' before multicasting events via the context: " + this);
        }
        return this.applicationEventMulticaster;
    }

     As you can see from the code above ,applicationEventMulticaster yes  AbstractApplicationContext Private member variable , So this multicast ( dispatcher ) How to get to ? In front of refresh One of the methods is initApplicationEventMulticaster() Method , It's called AbstractApplicationContext.initApplicationEventMulticaster()  To initialize the multicast ( dispatcher ) Of :

    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // from bean The factory looks for one bean by applicationEventMulticaster, If there is , Take it out of 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 {
            // without , Register a... In the container SimpleApplicationEventMulticaster, The name is applicationEventMulticaster, You can use it if you need to dispatch events 
            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() + "]");
            }
        }
    }   

  4. That's it  getApplicationEventMulticaster(), Say again multicastEvent(), Its role is to distribute Events , It is ApplicationEventMulticaster A method of interface , So it calls the implementation class SimpleApplicationEventMul

ticaster( The multicast device registered above ) Of multicastEvent Method :

public interface ApplicationEventMulticaster {

    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

}
//public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        // According to the message type, get all the corresponding listeners 
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            // If you can use multithreading to execute , We use multithreading to dispatch events asynchronously , How to execute the listener 
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }
}

  5. Finally, let's have a look at  invokeListener Method , It will get the listener to call back MyApplicationListener. onApplicationEvent Method , And then the console outputs information  :

    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            doInvokeListener(listener, event);// perform 
        }
    }
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            // Callback onApplicationEvent
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                // -> let's suppress the exception and just log a debug message.
                Log logger = LogFactory.getLog(getClass());
                if (logger.isTraceEnabled()) {
                    logger.trace("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }

  6. When the case is executed again 2 In the event of , from AbstractApplicationContext. publishEvent()( The above first 3 Step ) Start execution , Step or step multicastEvent Dispatch incident --> invokeListener Callback execution  onApplicationEvent;

  7. When the container is closed , Would call doClose(), There's a ContextClosedEvent event , The listener listens to the event console and outputs information :

    context.close();
    @Override
    public void close() {
        synchronized (this.startupShutdownMonitor) {
            doClose();
            // If we registered a JVM shutdown hook, we don't need it anymore now:
            // We've already explicitly closed the context.
            if (this.shutdownHook != null) {
                try {
                    Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                }
                catch (IllegalStateException ex) {
                    // ignore - VM is already shutting down
                }
            }
        }
    }
    protected void doClose() {
        // Check whether an actual close attempt is necessary...
        if (this.active.get() && this.closed.compareAndSet(false, true)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Closing " + this);
            }
            
            LiveBeansView.unregisterApplicationContext(this);

            try {
                // Publish shutdown event.
                publishEvent(new ContextClosedEvent(this));
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
            }

            // Stop all Lifecycle beans, to avoid delays during individual destruction.
            if (this.lifecycleProcessor != null) {
                try {
                    this.lifecycleProcessor.onClose();
                }
                catch (Throwable ex) {
                    logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
                }
            }

            // Destroy all cached singletons in the context's BeanFactory.
            destroyBeans();

            // Close the state of this context itself.
            closeBeanFactory();

            // Let subclasses do some final clean-up if they wish...
            onClose();

            // Reset local application listeners to pre-refresh state.
            if (this.earlyApplicationListeners != null) {
                this.applicationListeners.clear();
                this.applicationListeners.addAll(this.earlyApplicationListeners);
            }

            // Switch to inactive.
            this.active.set(false);
        }
    }

   8. It's on top of 4 In step dispatch event operation getApplicationListeners Got all the monitors , So what listeners are in the container ? From 1 Step by step  registerListeners() You can see , The container registers the multicast and listener before publishing the event , Here's the listener registration :

    protected void registerListeners() {
        // Register statically specified listeners first.
        // Take all the listeners from the container and add them to the multiplexer ,( There was no one at first , Skip the loop and perform the following steps )
        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!
        // If there is no , That is, the above traversal is not executed , The component name is obtained according to the type , Then get the corresponding component according to the component name and add it to the multiplexer 
        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 (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

     Let's see next  getApplicationListeners Method , Find out it's  AbstractApplicationEventMulticaster class , So at the beginning of the elaboration  getApplicationListeners Before the method , First look at it.  AbstractApplicationEventMulticaster A class is a class of what function .

      You can see from the above class diagram that  AbstractApplicationEventMulticaster It implements several interfaces , It's an event dispatcher , No 3 Click the initialization dispatcher mentioned in the  SimpleApplicationEventMulticaster It's actually inherited from AbstractApplicationEventMulticaster Of .

     Before looking at the source code of the listener below , Let's think about a question first : How the listener can only listen to the specified type of messages ? How to achieve , What's the way ?

     We can guess first :

      1) When you register a listener , Bind the listener to the message type ;( This argument can be seen in the following  addApplicationListener Implementation method analysis )

      2) On the air , According to the type of the message, find the listener with the specified type , But it's impossible to find every broadcast in all the monitors , The type binding between the listener and the message will be triggered during broadcast ;

      Okay , Let's take a closer look at these questions and guesses  getApplicationListeners Source code :

public abstract class AbstractApplicationEventMulticaster
        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    // Create a listener helper class , Used to store the set of listeners , Parameter is a prefilter listener 
    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
    //ListenerCacheKey Is a class based on event type and source type as key Used to store listener assistants 
    final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

    @Nullable
    private ClassLoader beanClassLoader;// Class loader 
    // Mutex listener helper class 
    private Object retrievalMutex = this.defaultRetriever;
    // Listener helper class ( Encapsulates a set of helper classes for a specific target listener , Allows efficient retrieval of pre filtered listeners , Instances of this helper are cached by time type and source type )
    private class ListenerRetriever {
        // Store event listeners , Orderly 、 Do not repeat 
        public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
        // Store event listeners bean name , Orderly , Do not repeat 
        public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
        // Whether to pre filter the monitor 
        private final boolean preFiltered;

        public ListenerRetriever(boolean preFiltered) {
            this.preFiltered = preFiltered;
        }
        // Get event listeners 
        public Collection<ApplicationListener<?>> getApplicationListeners() {
            // Create a... Of a specified size ApplicationListener Monitor List aggregate 
            List<ApplicationListener<?>> allListeners = new ArrayList<>(
                    this.applicationListeners.size() + this.applicationListenerBeans.size());
            allListeners.addAll(this.applicationListeners);
            // If you store a monitor bean name The set of is not empty 
            if (!this.applicationListenerBeans.isEmpty()) {
                // obtain IOC Container factory class 
                BeanFactory beanFactory = getBeanFactory();
                for (String listenerBeanName : this.applicationListenerBeans) {
                    try {
                        // Get specified bean name An example of a listener for 
                        ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        // Determine if it is a pre filtered listener or if the collection does not contain a listener instance, it will be added to the collection 
                        if (this.preFiltered || !allListeners.contains(listener)) {
                            allListeners.add(listener);
                        }
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        // Singleton listener instance (without backing bean definition) disappeared -
                        // probably in the middle of the destruction phase
                    }
                }
            }
            if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
                AnnotationAwareOrderComparator.sort(allListeners);
            }
            return allListeners;
        }
    }
    // technological process 1: When the published event type and event source type are the same as Map(retrieverCache) Medium key When the match ,
    // Will return directly value The list of listeners in as a match result , Usually this happens when the event is not the first release , It can avoid traversing all listeners and filtering ,
    // If the event is first released , Then the process will be executed 2.
    protected Collection<ApplicationListener<?>> getApplicationListeners(
        ApplicationEvent event, ResolvableType eventType) {
        // Event source , The object on which the event first occurred 
        Object source = event.getSource();
        // Event source class object 
        Class<?> sourceType = (source != null ? source.getClass() : null);
        // The cache key There are two dimensions : Source + Message type ( We can see from the sources that ApplicationEvent Input parameters of construction method )
        // Create a listener assistant based on event source and source type cacheKey
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

        // Quick check for existing entry on ConcurrentHashMap...
        // retrieverCache yes ConcurrentHashMap object ( Concurrent container , Using lock segmentation to ensure data security under multithreading ), So it's thread safe ,
        // ListenerRetriever There's a collection of listeners in , And some simple logic encapsulation , Call it the getApplicationListeners The collection of listening classes returned by the method is ordered (order Annotation sorting )
        ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
        // If retrieverCache Find the corresponding set of listeners in the , Immediately returned to 
        if (retriever != null) {
            return retriever.getApplicationListeners();
        }
        // If the class loader is null, Or the event source is secure in a given class loader context and the source type is null Or the source type is safe in the specified context 
        if (this.beanClassLoader == null ||
                (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                        (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
            // Fully synchronized building and caching of a ListenerRetriever
            // Lock listener helper object , Synchronize from ListenerRetriever Get the specified listener from the listener assistant 
            synchronized (this.retrievalMutex) {
                // After grabbing the lock, make a judgment again , Because it could be in front of BLOCK When , Another thread that grabs the lock has already set up the cache ,
                // To avoid being in BLOCK Other threads have already put the data into the cache 
                retriever = this.retrieverCache.get(cacheKey);
                if (retriever != null) {
                     // Returns the listener object stored in the listener assistant 
                    return retriever.getApplicationListeners();
                }
                retriever = new ListenerRetriever(true);
                //retrieveApplicationListeners Method to actually retrieve the listener for a given event and source type 
                Collection<ApplicationListener<?>> listeners =
                        retrieveApplicationListeners(eventType, sourceType, retriever);// technological process 2
                //retriever Put it in the buffer ( Update cache )
                this.retrieverCache.put(cacheKey, retriever);
                return listeners;
            }
        }
        else {
            // No ListenerRetriever caching -> no synchronization necessary
            //  nothing ListenerRetriever Monitor assistant  ->  No need to synchronize cache 
            return retrieveApplicationListeners(eventType, sourceType, null);
        }
    }
}

     From the above source code can be seen :

      1) In obtaining ApplicationListener The cache is used when , At the same time, there are cache updates and locks to ensure thread synchronization ( Double judgment also made ), So if you customize the broadcast , If multiple threads broadcast at the same time , There will be no problem of thread synchronization .  

      2) When sending a message, find all the corresponding listeners according to the type , In this way, a custom listener can only receive messages of the specified type .

        3) At the moment of broadcasting news , If a certain type of message cannot find the corresponding listener collection in the cache , Just call retrieveApplicationListeners Method to find all the listeners that meet the criteria , And then put it into this collection .

     Let's take a closer look at  retrieveApplicationListeners:

    private Collection<ApplicationListener<?>> retrieveApplicationListeners(
            ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
        // A list of matching listeners 
        List<ApplicationListener<?>> allListeners = new ArrayList<>();
        Set<ApplicationListener<?>> listeners;
        Set<String> listenerBeans;
        // Lock listener helper object 
        synchronized (this.retrievalMutex) {
            // Get the listener object stored in the listener assistant , duplicate removal 
            listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
            // Get the listener stored in the listener assistant bean Name set , duplicate removal 
            listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
        }

        // Add programmatically registered listeners, including ones coming
        // from ApplicationListenerDetector (singleton beans and inner beans).
        //// Traverse all the listeners 
        for (ApplicationListener<?> listener : listeners) {
            // The logic to determine whether the listener matches is in supportsEvent(listener, eventType, sourceType) in 
            if (supportsEvent(listener, eventType, sourceType)) {
                // Listener helper class is not empty , Add to the listener collection in the listener helper class 
                if (retriever != null) {
                    retriever.applicationListeners.add(listener);
                }
                // Put it in the list of matching listeners 
                allListeners.add(listener);
            }
        }

        // Add listeners by bean name, potentially overlapping with programmatically
        // registered listeners above - but here potentially with additional metadata.
        // The listener stored in the listener assistant bean The name set has values 
        if (!listenerBeans.isEmpty()) {
            // obtain IOC Containers 
            ConfigurableBeanFactory beanFactory = getBeanFactory();
            // Traverse 
            for (String listenerBeanName : listenerBeans) {
                try {
                    // The logic to determine whether the listener matches is in supportsEvent(listener, eventType, sourceType) in 
                    if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                        // Get specified bean name An example of a listener for 
                        ApplicationListener<?> listener =
                                beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        // If the matching listener list does not contain the instance obtained above and conforms to supportsEvent The logic of 
                        if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                            // Listener helper class is not empty 
                            if (retriever != null) {
                                //bean The name is a singleton , There are no duplicate listeners in the container 
                                if (beanFactory.isSingleton(listenerBeanName)) {
                                    // Add to the listener collection in the listener helper class 
                                    retriever.applicationListeners.add(listener);
                                }
                                else {
                                    // duplicate removal ,applicationListenerBeans yes LinkedHashSet
                                    retriever.applicationListenerBeans.add(listenerBeanName);
                                }
                            }
                            // Put it in the list of matching listeners 
                            allListeners.add(listener);
                        }
                    }
                    // The logic of whether the listener matches is not in supportsEvent(listener, eventType, sourceType) in , Remove from the listener helper class and the list of matching listeners 
                    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) {
                            retriever.applicationListeners.remove(listener);
                        }
                        allListeners.remove(listener);
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Singleton listener instance (without backing bean definition) disappeared -
                    // probably in the middle of the destruction phase
                }
            }
        }
        // Listeners sort 
        AnnotationAwareOrderComparator.sort(allListeners);
        if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
            retriever.applicationListeners.clear();
            retriever.applicationListeners.addAll(allListeners);
        }
        return allListeners;
    }

      Here is  supportsEvent Source code exploration :

    // First of all, the original ApplicationListener A layer of adapter is packaged into GenericApplicationListener,
    // It is convenient to use the methods defined in the interface to determine whether the listener supports the incoming event type or event source type 
    protected boolean supportsEvent(
            ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

        GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
                (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
        return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
    }
    
    public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {  
        // Determine whether the event type is supported 
        boolean supportsEventType(ResolvableType eventType); 

        // Determine whether the event source type is supported , The default is true, That is to say, the type of event source usually doesn't make sense for matching listeners 
        default boolean supportsSourceType(@Nullable Class<?> sourceType) {
            return true;
        }
    }
public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener {

    ......
    // The actual type of listener generics 
    @Nullable
    private final ResolvableType declaredEventType;

    
    // Determine whether the listener supports the event type , Because our listener instances are usually not SmartApplicationListener type 
    //eventType It's the type of event published 
    @Override
    @SuppressWarnings("unchecked")
    public boolean supportsEventType(ResolvableType eventType) {
        if (this.delegate instanceof SmartApplicationListener) {
            Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
            return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
        }
        else {
        //this.declaredEventType.isAssignableFrom(eventType) When the following two situations return true
        //    1.declaredEventType and eventType The same type 
        //    2.declaredEventType yes eventType The father type of 
        // As long as the actual type of the listener generic is the same as the published event type or its parent type , Then the listener will be successfully matched .
            return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
        }
    }
}

    9. Here's the flow chart :

  Four . Expand

  1. As can be seen from the above case , as long as bean Inherit  ApplicationEvent, And then use the container's publishEvent You can publish the event ( Similar to the above case 4), So there are other ways to make bean Do you have the same ability to publish events as a container ?

    The answer, of course, is yes , such as ApplicationEventPublisherAware This interface can make bean Ability to publish Events . Here is the source code of the interface :

public interface ApplicationEventPublisherAware extends Aware {

    void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);

}

      We can create one bean, Realized ApplicationEventPublisherAware Interface , Then the bean Of setApplicationEventPublisher Method will be called , Through this method, you can receive ApplicationEventPublisher type , With this help ApplicationEventPublisher You can send a message ;

    For example, the following example :

    1) Interface :

public interface UserEventRegisterService {
    /**
     *  Release events , Registered users 
     */
    void register();
}

    2) Interface implementation :

@Service
public class UserEventRegisterServiceImpl implements UserEventRegisterService {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void register() {
        User user = new User();
        user.setId(1);
        user.setName(" Zhang San ");
        user.setSex(" male ");
        applicationEventPublisher.publishEvent(user);
        System.out.println(" end .");
    }
}

    3) Custom listener : have access to  @EventListener Annotations to implement custom listeners ( This note is described in detail below )

@Component
public class UserEventListener {
    @EventListener(condition = "#user.id != null")// Listen when the user id Events that are not empty 
    public void handleEvent(User user){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(" What the listener is listening to :"+user);
    }
}

    4) Configuration class :

@ComponentScan("com.hrh.ext")
@Configuration
public class ExtConfig {

}

    5) test :

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExtConfig.class);
        UserEventRegisterService service = context.getBean(UserEventRegisterService.class);
        service.register();
        context.close();
    }
====== Running results ======
 What the listener is listening to :User [id=1, name= Zhang San , sex= male ]
 end .

  2. How listeners are added to the container ? We take from the container refresh Let's start , Found in refresh There are prepareBeanFactory(beanFactory) Method , For all bean We have a post processor ApplicationListenerDetector:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 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);

            .........
        }
}
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    ..........

    // Register early post-processor for detecting inner beans as ApplicationListeners.
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    ..........
}

     Let's take a look at  ApplicationListenerDetector Class source code , Because it's a post processor ( It is not clear that the post processor can be viewed Last article , There is a brief introduction to ), So it has  postProcessBeforeInitialization and  postProcessAfterInitialization Two methods , These two methods are for all bean Intercept after instantiation :

    private final transient AbstractApplicationContext applicationContext;
    
    private final transient Map<String, Boolean> singletonNames = new ConcurrentHashMap<>(256);
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // If bean yes ApplicationListener type 
        if (bean instanceof ApplicationListener) {
            // potentially not detected as a listener by getBeanNamesForType retrieval
            // Judge bean Not in the concurrency container 
            Boolean flag = this.singletonNames.get(beanName);
            if (Boolean.TRUE.equals(flag)) {
                // singleton bean (top-level or inner): register on the fly
                // Register listener , In fact, it is stored in member variables applicationEventMulticaster Member variables of defaultRetriever Set applicationListeners in 
                this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
            }
            else if (Boolean.FALSE.equals(flag)) {
                if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
                    // inner bean with other scope - can't reliably process events
                    logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
                            "but is not reachable for event multicasting by its containing ApplicationContext " +
                            "because it does not have singleton scope. Only top-level listener beans are allowed " +
                            "to be of non-singleton scope.");
                }
                this.singletonNames.remove(beanName);
            }
        }
        return bean;
    }
    public void addApplicationListener(ApplicationListener<?> listener) {
        Assert.notNull(listener, "ApplicationListener must not be null");
        if (this.applicationEventMulticaster != null) {
            this.applicationEventMulticaster.addApplicationListener(listener);
        }
        this.applicationListeners.add(listener);
    }    

     As shown above , If at present bean Realized ApplicationListener Interface , Will call this.applicationContext.addApplicationListener Method will be current bean Sign up to applicationContext In the set of listeners for , If there is a broadcast in the back, you can find these monitors directly , Call the onApplicationEvent Method ;  

     Let's take a look at the details  addApplicationListener Method , It is implemented in  AbstractApplicationEventMulticaster Class :

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    // Lock listener helper object 
    synchronized (this.retrievalMutex) {
        // Explicitly remove target for a proxy, if registered already,
        // in order to avoid double invocations of the same listener.
        //  If you have already registered , Explicitly delete the registered listener object 
        //  To avoid calling duplicate listener objects 
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            // If because AOP Causes the creation of a proxy for the listening class , Then clear the proxy class from the registration list 
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
        }
        // Add listeners to the collection defaultRetriever.applicationListeners in , This is a LinkedHashSet example 
        this.defaultRetriever.applicationListeners.add(listener);
        // Clear listener assistant cache Map
        this.retrieverCache.clear();
    }
}

      You can see... On it , If the object is AOP Generated proxy class , It needs to be removed , Because AOP It is realized by proxy technology , At this point, it may be through CGLIB Generated the proxy class of the listening class , If an instance of this class is registered in the listener collection , Then when broadcasting, two listener instances will be taken out according to the message type , At that time, a message is consumed by two instances , So you need to clean up the proxy class first .

     At the same time, it also demonstrates a point that : The so-called registered listener , It's just a way of ApplicationListener Put an implementation class of LinkedHashSet Set , There are no operations related to message types , therefore , The listener is registered without binding the message type to the listener ;

  3. Asynchronous listening event case : The normal event notification is  ContextRefreshedEvent --> EmailEvent --> ContextClosedEvent

// Turn on Asynchronous Support 
@EnableAsync
@Component
public class EmailListener implements ApplicationListener {
    @Async// Asynchronous execution 
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof EmailEvent) {

            EmailEvent emailEvent = (EmailEvent) event;
            System.out.println(" mailing address :" + emailEvent.getAddress());
            System.out.println(" Email content :" + emailEvent.getText());
        } else {
            System.out.println(" The container itself event :" + event);
        }
        // Distinguish by thread name 
        System.out.println(event+ ":" + Thread.currentThread().getName());
    }
}
======= Test run results : From the following running results, it can be seen that it was executed asynchronously =======
 mailing address :249968839@qq.com
 Email content :This is a event test
com.hrh.ext.EmailEvent[source=hello]:SimpleAsyncTaskExecutor-2
 The container itself event :org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020]
 The container itself event :org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020]
org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020]:SimpleAsyncTaskExecutor-1
org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2e5d6d97, started on Mon Nov 09 22:29:09 CST 2020]:SimpleAsyncTaskExecutor-3

   5.Spring Transaction monitoring mechanism --- Use @TransactionalEventListener Handle the database transaction and execute the operation after the transaction is submitted successfully

     In the project , It is often necessary to perform database operations after , Send messages or events to call other components asynchronously to perform corresponding operations , for example : Send activation code after user registration 、 Send update event after configuration modification .

     however , If the operation of the database has not been completed , At this time, the method called asynchronously queries the database and finds that there is no data , There will be problems .

     The following pseudo code example : 

void saveUser(User u) {
    // Save user information 
    userDao.save(u);
    // Trigger save user event 
    applicationContext.publishEvent(new SaveUserEvent(u.getId()));
}

@EventListener
void onSaveUserEvent(SaveUserEvent event) {
    // Get information from the event ( user id)
    Integer id = event.getEventData();
    // Query the database , Get users ( At this point, if the user has not yet inserted the database , It returns null )
    User u = userDao.getUserById(id);
    // Null pointer exception may be reported here !
    String phone = u.getPhoneNumber();
    MessageUtils.sendMessage(phone);
}

          In order to solve the above problems ,Spring There are two ways for us : @TransactionalEventListener annotation and Transaction synchronization manager TransactionSynchronizationManager, So that we can trigger an event after the transaction is committed .

    @TransactionalEventListener Use cases of annotations : Only after the current transaction is committed , Will execute the method of the event listener

//phase The default is AFTER_COMMIT, There are four enumerations :BEFORE_COMMIT,AFTER_COMMIT,AFTER_ROLLBACK,AFTER_COMPLETION
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) void onSaveUserEvent(SaveUserEvent event) { Integer id = event.getEventData(); User u = userDao.getUserById(id); String phone = u.getPhoneNumber(); MessageUtils.sendMessage(phone); }

    TransactionSynchronizationManager Use case of :@TransactionalEventListener It's done at the bottom

@EventListener
void onSaveUserEvent(SaveUserEvent event) {
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            Integer id = event.getEventData();
            User u = userDao.getUserById(id);
            String phone = u.getPhoneNumber();
            MessageUtils.sendMessage(phone);
        }
    });
}

版权声明
本文为[Code ape hand]所创,转载请带上原文链接,感谢

Scroll to Top