registerListeners() of Spring source code and publish subscribe mode

registerListeners() of Spring source code and publish subscribe mode

Hello, I'm classmate Tian, a programmer.

Today, let's explain the registerListeners() method in the refresh() method, which is often referred to as the publish subscribe mode of Spring. This paper first gives an example of publish subscribe mode, then explains the principles of the four publish subscribe modes, and gives an example of the observer mode on which the publish subscribe mode depends. Finally, it leads to a large number of applications of this mode in Springboot.

As usual, put a copy of the source code of the refresh() method, and the registerListeners() method is located in the seventh position of the method.

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //1. Preparation before refresh
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      //2. Bean factory will be initialized, beans will be loaded, and beans will be registered
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      //3. Set the class loader of BeanFactory, add several beanpostprocessors, and manually register several special beans
      prepareBeanFactory(beanFactory);

      try {
         //4. Template method
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         //Execute BeanFactory post processor
         invokeBeanFactoryPostProcessors(beanFactory);

         // 5,Register bean processors that intercept bean creation.
         //Register bean post processor
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         //internationalization
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         //6. Template method -- springboot implements this method
         onRefresh();

         // Check for listener beans and register them.
         //7. Register listener
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         //8. It is important to complete the initialization * * method of the bean factory**********************************************
         finishBeanFactoryInitialization(beanFactory);

         //9, Last step: publish corresponding event.
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

Let's take a general look at the source code of the registerListeners() method.

protected void registerListeners() {
        // Register statically specified listeners first.
        // First, register the static specified listener, and register the special event listener instead of the bean in the configuration
        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!
        // Factorybeans will not be initialized here. We need to keep all ordinary beans
        // These bean s will not be instantiated so that the post processor can perceive them
        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...
        // Now there is an event broadcast group to publish the previous application events
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

This method is very simple, that is, two loop traversal. Register the listener defined by Spring through hard coding into the container, and then register our custom listener into the container. Through these direct statements, there is some weakness. Let's write a simple example of distribution subscription for easy understanding.

Four roles are required in publish subscribe mode:

ApplicationEvent: event. Each implementation class represents a kind of event and can carry data. Abstract class.
ApplicationListener: event listener, used to receive event processing time. Interface.
Applicationeventmulticast: event manager, which can register (add) / remove / publish events. Used for event listener registration and event broadcasting. Interface.
ApplicationEventPublisher: event publisher, which entrusts the event manager ApplicationEventMulticaster to complete event publishing.

event:

@Component
public class MyEvent extends ApplicationEvent {

    private static final long serialVersionUID = 1L;
    
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public MyEvent(Object source) {
        super(source);
    }
}

Event listener:

@Component
public class MyEventListener implements ApplicationListener<MyEvent> {

    @EventListener //@The EventListener annotation implements event listening
    @Override
    public void onApplicationEvent(MyEvent event) {
        Object msg = event.getSource();
        System.out.println("Custom event listener( MyEventListener1)Received the published message: " + msg);

    }
}

Event Publisher:

public static void main(String[] args) {
        System.out.println(1);
        ApplicationContext ac =new AnnotationConfigApplicationContext(MyEventListener.class);
        MyEvent myEvent=new MyEvent(new Object());
        ac.publishEvent(myEvent);

    }

So where is the event manager?

Let's make a breakpoint on the registerListeners() method to see how our custom events work in Spring.

The first loop is Spring's default listener, which is empty by default.

Our custom event listener is loaded into the applicationeventmulticast in the second cycle. It is obvious that applicationeventmulticast is our event manager.

We're going through their relationship in detail.

Note: the broadcaster and the manager above mean the same thing

At this stage, the event manager and listener are initialized and registered in the Spring container, and then the listener mode can be implemented to listen and process the release of events.

In, it is ac.publishEvent(myEvent); Method is used to process the business after publishing the event.

ApplicationContext ac =new AnnotationConfigApplicationContext(MyEventListener.class);
        MyEvent myEvent=new MyEvent(new Object());
        ac.publishEvent(myEvent);

The subscriber - to - subscriber mode is the implementation of the event - to - observer mode.

By the way, this paper briefly describes the observer model, which is convenient for readers to better understand.

The definition of Observer mode: it refers to the one to many dependency between multiple objects. When the state of an object changes, all objects that depend on it are notified and automatically updated.

public class RMBrateTest {
    public static void main(String[] args) {
        Rate rate = new RMBrate();
        Company watcher1 = new ImportCompany();
        Company watcher2 = new ExportCompany();
        rate.add(watcher1);
        rate.add(watcher2);
        rate.change(10);
        rate.change(-9);
    }
}
//Abstract target: exchange rate
abstract class Rate {
    protected List<Company> companys = new ArrayList<Company>();
    //Add observer method
    public void add(Company company) {
        companys.add(company);
    }
    //Delete observer method
    public void remove(Company company) {
        companys.remove(company);
    }
    public abstract void change(int number);
}
//Specific target: RMB exchange rate
class RMBrate extends Rate {
    public void change(int number) {
        for (Company obs : companys) {
            ((Company) obs).response(number);
        }
    }
}
//Abstract Observer: Company
interface Company {
    void response(int number);
}
//Specific observer 1: import company
class ImportCompany implements Company {
    public void response(int number) {
        if (number > 0) {
            System.out.println("Appreciation of RMB exchange rate" + number + "Basis points, reducing the cost of imported products and improving the profit margin of import companies.");
        } else if (number < 0) {
            System.out.println("Depreciation of RMB exchange rate" + (-number) + "Basis points, increased the cost of imported products and reduced the profit margin of import companies.");
        }
    }
}
//Specific observer 2: export companies
class ExportCompany implements Company {
    public void response(int number) {
        if (number > 0) {
            System.out.println("Appreciation of RMB exchange rate" + number + "Basis points, reducing the income of export products and reducing the sales profit margin of export companies.");
        } else if (number < 0) {
            System.out.println("Depreciation of RMB exchange rate" + (-number) + "Basis points, which has increased the income of export products and the sales profit margin of export companies.");
        }
    }
}

The change of exchange rate is an event. When the event occurs, its observers, export companies and import companies will make corresponding changes.

The book returns to the registerListeners() method. At this time, all listeners are registered in the container. After publishEvent() publishes the event, it will execute the business logic in our listener.

It is said that a large number of publish and subscribe modes are applied in Springboot. With an attitude of thirst for knowledge, let's go to Springboot and hit a breakpoint on registerListeners() to see the difference between it and the breakpoint on Spring.

In Spirng, the listener is empty by default. At that time, there were as many as 15 listeners in Springboot, which is worthy of being an enhanced version of Spring. We won't delve into what these listeners are for. They will be gradually disassembled in Springboot.

Well, today's analysis of the method of registerListeners() is over.

Keywords: Java Spring Spring Boot Back-end

Added by jreed2132 on Mon, 07 Mar 2022 09:45:46 +0200