编程知识 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 Characters :

    1.Event Source: Specific event sources , For example , You choose one of the interfaces button Button , So this button is the source of the event , 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 objects , Used in the corresponding method of the monitor , As an argument ;

    3.Event Listener: Event monitor , When it listens event object When it came into being , It handles the call to the corresponding method ;

     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 an event into an event object of the corresponding type , And pass it to the appropriate listener registered with the event source ;

      3) When the event object is passed 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 ” Relationship of .

   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 , For any event , Which components can produce 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 of event center AppCreationEvent 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 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 for all event listener interfaces that must extend the suite . 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 Interface, etc ;

   So much said , Now let's get back to business , Speaking of Spring The event mechanism of , You have to start from Spring The container began to talk about , stay IOC The start-up 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 release the event .

public void refresh() throws BeansException, IllegalStateException {
        // A lock , Otherwise  refresh()  It's not over yet , You start or destroy the container again , That would be 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 in the context , May be in spring Later versions will expand the suite .
                onRefresh();

                // Register listeners 
                registerListeners();

                
                // Object creation : Initialize all the remaining ( Not lazy loaded ) Singleton objects 
                finishBeanFactoryInitialization(beanFactory);

                // Rearrange and complete the work , Include initialization LifecycleProcessor, Release, rearrange, complete events, etc 
                finishRefresh();
        }
            .................
}

    stay  AbstractApplicationContext Class finishRefresh Method , It will post ( Broadcasting ) 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();

        // waive ( Broadcasting ) A message , Type ContextRefreshedEvent representative Spring End of container initialization 
        publishEvent(new ContextRefreshedEvent(this));

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

    So , When Spring IOC The container is loaded and processed bean After that , It also gives us an opportunity , You can do what you want to do . This is also Spring IOC Where containers are provided for external extensions , We can use this extension suite mechanism , To achieve some special business needs .

   Let's take our bean Realize   ApplicationListener Interface , So when the event is released , 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 to achieve ah , 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 are processed , Now InitializingBean The interface is in place .( See the following example 3)

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

  ApplicationListener Monitoring 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 releases 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 , When a certain operation is completed, some event action will be issued . For example, in the above monitoring ContextRefreshedEvent event , When all bean This event will be triggered after both initialization and successful loading , Realize ApplicationListener<ContextRefreshedEvent> The interface can receive listening actions , How to implement your own business logic .

   Here are some Spring Built in events for :

    • ContextRefreshedEvent:ApplicationContext  When initialized or reorganized , The event was released . This can also be done in  ConfigurableApplicationContext The interface uses refresh() Method to happen . Initialization here means : be-all Bean Successfully loaded , Post processor Bean Detected and activated , all Singleton Bean To be itemized by presupposition ,ApplicationContext The container is ready for use ;【 Container rearrangement completes all bean It's fully established and will release this event 】
    • ContextStartedEvent: When using  ConfigurableApplicationContext (ApplicationContext Subinterfaces ) In the interface start() Method start  ApplicationContext  When , The event was released . You can investigate your database , Or you can restart any stopped application after receiving this event ;
    • ContextStoppedEvent: When using  ConfigurableApplicationContext  In the interface stop() stop it  ApplicationContext  When , Release the incident . You can do the necessary cleaning up after receiving this incident ;
    • ContextClosedEvent: When using  ConfigurableApplicationContext  In the interface close() Method off  ApplicationContext  When , The event was released . A context has reached the end of its life cycle , It can't be reorganized 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 . Can only be applied to the use of DispatcherServlet Of Web Apply . In the use of Spring As the front end MVC When the controller is , 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 : Combined with the case of the last article , Implement listener monitoring Spring Container changes

@Configuration
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    // When the container releases this event , Method trigger 
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println(" Current events received :"+event);
    }
}
========= Test execution results =========
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean 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 Number of :12
[MyBeanFactoryPostProcessor] Calling postProcessBeanFactory
[MyBeanFactoryPostProcessor] At present beanFactory share 12 One 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】 Over !
[MyBeanPostProcessor] Post processor processing bean=【myApplicationListener】 Start 
[MyBeanPostProcessor] Post processor processing bean=【myApplicationListener】 Over !
Person There are reference constructors :[name= Zhang San ,sex= male ]
[Person] Calling BeanNameAware Of setBeanName The method :person
[Person] Calling BeanFactoryAware Of setBeanFactory The method :org.s[email protected]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] Calling Initailization Of afterPropertiesSet The method 
[MyBeanPostProcessor] Post processor processing bean=【person】 Over !
[MyBeanPostProcessor] Post processor processing bean=【color】 Start 
[MyBeanPostProcessor] Post processor processing bean=【color】 Over !
 Current events received :org.springframework.context.event.ContextRefreshedEvent[source=org.spring[email protected]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.spring[email protected]2e817b38, started on Tue Nov 03 22:17:59 CST 2020]
[Person] Calling DisposableBean Of destroy The method 

   2. Custom release an event , The 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 the event , As long as the container has the release of relevant 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 the event 
        context.publishEvent(new ApplicationEvent(new String(" Custom events ")) {
            });
        context.close();
    }
========= Test execution results =========
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean Number of :11
...............
[MyBeanPostProcessor] Post processor processing bean=【color】 Over !
 Current events received :org.springframework.context.event.ContextRefreshedEvent[source=org.spring[email protected]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.spring[email protected]2e817b38, started on Tue Nov 03 22:40:39 CST 2020]
[Person] Calling DisposableBean Of destroy The method 

     From the above execution results we can see that , We add an event to the container , When we release events , The listener will listen to the event and release 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 comments :

// 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 complete  <<<<<");
            // 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 execution results =======
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean Number of :14
................
[MyBeanPostProcessor] Post processor processing bean=【color】 Start 
[MyBeanPostProcessor] Post processor processing bean=【color】 Over !
>>>>> Spring Initialization complete  <<<<<
>>>>> {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] Calling DisposableBean Of destroy The method 

  4. Custom event and monitor and release

    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(" Email 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());
        // Build a ApplicationEvent thing 
        EmailEvent event = new EmailEvent("hello","[email protected]","This is a event test");
        // Actively trigger the event 
        context.publishEvent(event);
        context.close();
    }
======= Test execution results =======
[MyBeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry--->bean Number of :15
.............
>>>>> Spring Initialization complete  <<<<<
>>>>> {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.spring[email protected]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 ]
 Email address :[email protected]
 Email content :This is a event test
 The container itself event :org.springframework.context.event.ContextClosedEvent[source=org.spring[email protected]2e5d6d97, started on Fri Nov 06 22:55:23 CST 2020]
[Person] Calling 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. Let's see first  ContextRefreshedEvent How the event was released , Or from the familiar formula refresh() Speak of , When the container is rearranged , You can see it calling  finishRefresh() To rearrange the execution events :

public void refresh() throws BeansException, IllegalStateException {
        // A lock , Otherwise  refresh()  It's not over yet , You start or destroy the container again , That would be 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 in the context , May be in spring Later versions will expand the suite .
                onRefresh();

                // Register listeners 
                registerListeners();

                
                // Object creation : Initialize all the remaining ( Not lazy loaded ) Singleton objects 
                finishBeanFactoryInitialization(beanFactory);

                // Rearrange and complete the work , Include initialization LifecycleProcessor, Release, rearrange, complete events, etc 
                finishRefresh();
        }
            .................
}

  2. stay AbstractApplicationContext.finishRefresh Methods will publish ( Broadcasting ) 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();

        // waive ( Broadcasting ) A message , Type ContextRefreshedEvent representative Spring End of container initialization 
        publishEvent(new ContextRefreshedEvent(this));

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

  3. Go on  publishEvent Methods will find that they are executed getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType) To broadcast a message :

    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 The method will put earlyApplicationEvents Set to empty ,( 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 release events 
        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }
    

    PS:publishEvent The way is to release ( Broadcasting ) The core competence of service , And it's defined in  ApplicationEventPublisher In the interface ,ApplicationContext The interface inherits  ApplicationEventPublisher, therefore  AbstractApplicationContext Abstract class (ApplicationContext Interface implementation class ) This method is implemented , It also has the ability to transmit broadcasting .

     above  getApplicationEventMulticaster() What is it ? We need to know more about , Its role is to get the event multicast ( The dispenser ), Passing 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 above code ,applicationEventMulticaster yes  AbstractApplicationContext Private member variables of , So this multicast device ( The dispenser ) How did you get it ? In front of refresh One of the methods is initApplicationEventMulticaster() Method , It's a call AbstractApplicationContext.initApplicationEventMulticaster()  To initialize the multicast ( The dispenser ) Of :

    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // From bean The factory inquires if there is one bean For 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 {
            // If not , 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(), Let's talk about multicastEvent(), Its role is to distribute Events , It is ApplicationEventMulticaster An interface method , 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 , Just 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 , 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);// Execute 
        }
    }
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            // Call back 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 executing , The steps are still multicastEvent Dispatch incident --> invokeListener Call back to execute  onApplicationEvent;

  7. When the container is closed , Will 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 are the listeners in the container ? From the 1 Step by step  registerListeners() You can see , The container registers the multicast and listener before releasing the event , Here's the listener registration :

    protected void registerListeners() {
        // Register statically specified listeners first.
        // Get 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, according to the component name, the corresponding component is obtained and added 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 take a look at  getApplicationListeners Method , Found out that it was  AbstractApplicationEventMulticaster Class , So at the beginning of the elaboration  getApplicationListeners Before the method , Let's see first  AbstractApplicationEventMulticaster A class is a class for what purpose .

      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 you look at the get listener source code below , Let's start with a question : How can the listener only listen to the message of the specified type ? 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 examined in the following  addApplicationListener Implementation method analysis )

      2) On the radio , 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 The original code of :

public abstract class AbstractApplicationEventMulticaster
        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    // Create a listener helper class , Used to store a set of listeners , Whether the argument is a prefilter listener 
    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
    //ListenerCacheKey Is a class based on event type and source type key To store the listener helper 
    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 specified size of ApplicationListener The 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()) {
                // Get IOC Container factory class 
                BeanFactory beanFactory = getBeanFactory();
                for (String listenerBeanName : this.applicationListenerBeans) {
                    try {
                        // Get specified bean name Monitor example item of 
                        ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        // Determine if it is a prefiltered listener or if the set does not contain a listener instance item, 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 released event type and event source type are the same as Map(retrieverCache) Medium key When matching ,
    // Will return directly to value The list of listeners in as a match result , Usually this happens when the event is not released for the first time , It can avoid traversing all listeners and filtering ,
    // If the event is released for the first time , 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 thing 
        Class<?> sourceType = (source != null ? source.getClass() : null);
        // It's fast key There are two dimensions : Source of information + Message type ( As for the source of information, we can see ApplicationEvent Input parameters of construction method )
        // Create a listener assistant based on the event source and source type cacheKey
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

        // Quick check for existing entry on ConcurrentHashMap...
        // retrieverCache yes ConcurrentHashMap thing ( Concurrent containers , 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 , Calling it getApplicationListeners The set 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 the 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 the 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 the given event and source type 
                Collection<ApplicationListener<?>> listeners =
                        retrieveApplicationListeners(eventType, sourceType, retriever);// technological process 2
                //retriever Put it in the cache ( 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);
        }
    }
}

     You can see from the source code above :

      1) Getting 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 thread synchronization problem .  

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

        3) At the moment of broadcasting a message , If the message of a certain type 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 the listener helper object 
        synchronized (this.retrievalMutex) {
            // Gets 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()) {
            // Get IOC Containers 
            ConfigurableBeanFactory beanFactory = getBeanFactory();
            // Traversal 
            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 Monitor example item of 
                        ApplicationListener<?> listener =
                                beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        // If the matching listener list does not contain the example items 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 Explore the original code :

    // First of all, the original ApplicationListener Package the interface card into GenericApplicationListener,
    // Then use the method 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 has no meaning for judging the matching listener 
        default boolean supportsSourceType(@Nullable Class<?> sourceType) {
            return true;
        }
    }
public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener {

    ......
    // The actual type of the listener generic 
    @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 released 
    @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 type is the same 
        //    2.declaredEventType yes eventType The parent type of 
        // As long as the actual type of the listener generic is the same as the released 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 . Expansion Kit

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

    The answer, of course, is , such as ApplicationEventPublisherAware This interface makes bean Have the ability to release events . Here is the source code of the interface :

public interface ApplicationEventPublisherAware extends Aware {

    void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);

}

      We can build a bean, Achieved ApplicationEventPublisherAware Interface , So bean Of setApplicationEventPublisher The method will be called , Through this method, we can receive ApplicationEventPublisher Type reference , By this ApplicationEventPublisher You can send a message ;

    For example, the following example :

    1) Interface :

public interface UserEventRegisterService {
    /**
     *  Release the event , 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();
    }
====== Execution 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 Rearrange and start to look , Found in refresh There is 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));

    ..........
}

     Next, let's see  ApplicationListenerDetector Class's original code , Because it's a post processor ( It's not clear about the viewability of the post processor , There is a brief introduction to ), So it has  postProcessBeforeInitialization and  postProcessAfterInitialization Two ways , These two methods are for all bean After instantiation, intercept operation :

    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 listeners , In fact, it is stored in member variables applicationEventMulticaster Member variables for 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 current bean Achieved 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 each listener's onApplicationEvent Method ;  

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

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    // Lock the 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 duplicate calls to the listener object 
        Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
        if (singletonTarget instanceof ApplicationListener) {
            // If it's because of AOP The creation of a class listening to the proxy results in , 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 For example 
        this.defaultRetriever.applicationListeners.add(listener);
        // Clear the listener assistant cache Map
        this.retrieverCache.clear();
    }
}

      You can see... On it , If the object is made by 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 two listener instances will be taken out according to the message type , At that time, a message is consumed by two examples , 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 , In fact, it's to put ApplicationListener Put an implementation class of LinkedHashSet Set , There are no operations related to the message type here , therefore , The listener is registered without binding the message type to the listener ;

  3. Asynchronous monitoring 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(" Email 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 execution results : From the following execution results, we can see that the execution is asynchronous =======
 Email address :2499688
	 
   
          
   
   
   
   
        

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

Scroll to Top