An overview,

Previously, we talked about the extension of beans. This article will discuss the extension of events

ApplicationListener is used to listen for events that occur in the container. Whenever an event occurs, the listener’s callback is triggered to complete the development of the event-driven model

This is outlined by two questions: What is an event? How is Spring implemented?

[1] What is an event

An event is an action that can be recognized by a control, such as pressing ok, selecting a radio button, or a check box. Each control has its own events that can be identified, such as the loading, clicking, double-clicking events of the form, the text changes of the edit box (text box), and so on. Events include system events and user events. System events are triggered by the system, such as every 24 hours a bank customer’s deposit date is increased by one day. User events are fired by the user, such as when the user clicks a button to display specific text in a text box. Event-driven controls perform a function.

[2] How to achieve

Spring also provides support for the event mechanism. After an event is published, the corresponding listener listens to it and executes the corresponding method. And with many events already provided in Spring, ApplicationEvent is arguably the top-level parent of Spring events. ApplicationListener is the listener’s top-level interface. When an event is triggered, the onApplicationEvent method is executed

If we’re going to write a listener, we’re going to write the class that implements the listener interface. ApplicationEvent generics are the class that we’re going to listen for, so we’re going to listen for, or publish, ApplicationEvent and its children. By looking at the ApplicationEvent class, We found the following events:

  1. ContextClosedEvent: Closes the container from publishing this event
  2. ContextRefreshedEvent: Container refresh completes publishing this event (all beans are instantiated and created)
  3. ContextStoppedEvent: Releases this event when the container stops
  4. ContextStartedEvent: Releases this event when the container begins execution

Implementation steps:

  1. Write a listener (ApplicationListener implementation class) to listen for an event (ApplicationEvent and its subclasses)
  2. Add listeners to the container
  3. Whenever there is an event published in the container, we can listen for that event, which is the child event mentioned above
  4. Custom publishing an event: applicationContext. PublishEvent ()

Ii. Case analysis

// Start the test class
@Test
public void TestMain(a){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    // Publish an event yourself
    applicationContext.publishEvent(new ApplicationEvent("Self-published events") {}); applicationContext.close(); }// ApplicationListener implementation class
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    // This method fires when the event is published in the container
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("Events received:"+ applicationEvent); }}/ / configuration class
@Configuration
@ComponentScan("listener")
public class AppConfig {}Copy the code

Run the startup class and the output is as follows:

  • When the container is started, the container refresh completion event, known as ContextRefreshedEvent, is executed
  • When the container is closed, the container closure event, ContextClosedEvent, is executed
  • In the launch class, publishEvent is used to publish events. When this method is executed, the ApplicationListener listens for the event and the onApplicationEvent is called back

Third, source code analysis

In the above case, three events are received: ContextRefreshedEvent, ContextClosedEvent, and MainTest$1[source= self-published event]. OnApplicationEvent = onApplicationEvent = onApplicationEvent = onApplicationEvent

[1] Event release

Through Debug, we can see that the first receive the ContextRefreshedEvent event, the following let’s analyze how to release the ContextRefreshedEvent according to the method call stack

Container to create objects, call the refresh () method — > finishRefresh () method — > publishEvent () method, called getApplicationEventMulticaster () method to obtain the event of multicast, is distributed

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    Object applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) { eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType(); }}if (this.earlyApplicationEvents ! =null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        // Get the multicast of the event, that is, the dispatcher
        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }

    if (this.parent ! =null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event); }}}Copy the code

Call multicastEvent for event dispatch

  • Get all applicationListeners for traversal
  • Determine if you can use executor asynchronously, and if you can use Executor asynchronously, you can customize whether to use executor asynchronously
  • Otherwise, the data is distributed simultaneously
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = eventType ! =null ? eventType : this.resolveDefaultEventType(event);
    Executor executor = this.getTaskExecutor();
    Iterator var5 = this.getApplicationListeners(event, type).iterator();

    while(var5.hasNext()) {
        // Get ApplicationListener for traversalApplicationListener<? > listener = (ApplicationListener)var5.next();// Determine whether executor can be used asynchronously, and if so, executor can be used asynchronously
        if(executor ! =null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            // Otherwise, the execution will be synchronized
            this.invokeListener(listener, event); }}}Copy the code

Execute the invokeListener method and get the Listener callback to the onApplicationEvent method

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        // Execute the invokeListener method and get the listener callback onApplicationEvent method
        listener.onApplicationEvent(event);
    } catch (ClassCastException var6) {
        String msg = var6.getMessage();
        if(msg ! =null&&!this.matchesClassCastMessage(msg, event.getClass())) {
            throw var6;
        }

        Log logger = LogFactory.getLog(this.getClass());
        if (logger.isTraceEnabled()) {
            logger.trace("Non-matching event type for listener: "+ listener, var6); }}}Copy the code

Distributing device getApplicationEventMulticaster [2] for events

Container to create objects, call the refresh () method — > initApplicationEventMulticaster () method, which initializes the ApplicationEventMulticaster

  • First from the container to find whether there is ID for “applicationEventMulticaster” components
  • If yes, the component is obtained by means of getBean
  • If not, create a simple ApplicationEventMulticaster
  • Will create a single instance of the registered to the container bean, so that we can be distributed to events in other components, automatic injection of the applicationEventMulticaster
protected void initApplicationEventMulticaster(a) {
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    / / from the bean factory to find if there's any ID for "applicationEventMulticaster" components
    if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
        // Get the component
        this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); }}else {
        / / if not by themselves to create a simple ApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        // Register the created bean into the container's singleton bean
        beanFactory.registerSingleton("applicationEventMulticaster".this.applicationEventMulticaster);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]"); }}}Copy the code

[3] What listeners are in the container

The container creates objects, calls the refresh() method — >registerListeners() method, and registers listeners

  • GetApplicationListeners: Gets all listeners
  • Get all the Listener components of type ApplicationListener from the container
  • Add components to getApplicationEventMulticaster distributing device, distribute in the registered to
protected void registerListeners(a) {
    // Get all listeners
    Iterator var1 = this.getApplicationListeners().iterator();

    while(var1.hasNext()) { ApplicationListener<? > listener = (ApplicationListener)var1.next();this.getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // Get all ApplicationListener components from the container
    String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true.false);
    String[] var7 = listenerBeanNames;
    int var3 = listenerBeanNames.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String listenerBeanName = var7[var4];
        / / add components to getApplicationEventMulticaster distributing device, distribute in the registered to
        this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if(! CollectionUtils.isEmpty(earlyEventsToProcess)) { Iterator var9 = earlyEventsToProcess.iterator();while(var9.hasNext()) {
            ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
            this.getApplicationEventMulticaster().multicastEvent(earlyEvent); }}}Copy the code

Four,

Event Release process:

  1. Refresh () : The container creates the object
  2. FinishRefresh () : The container is refreshed
  3. PublishEvent (new ContextRefreshedEvent()) : Publish an event that takes the container refresh completion event as an argument
    1. GetApplicationEventMulticaster () : access to the events of multicast (distributed), is to send events to multiple listeners that they perceived at the same time
    2. MulticastEvent: dispatches events
    3. All applicationListeners are retrieved through a loop and judged
      1. If executors are available, asynchronous dispatch using Executors is supported
      2. Otherwise, execute the Listener method synchronously and get the Listener callback to the onApplicationEvent method