[learning notes of Spring column] - container events and event listeners

This article is learning Chapter 10 of Spring hand column Notes, mainly record some of my debug debugging process, which is convenient for later review. For specific study, you can go to this column and highly recommend it.

target

There is an Event function in Spring, which can provide Event definition, publishing and listening to events to complete some custom actions. For example, you can define a new user registration Event. When a user completes the registration, send some coupons and SMS reminders to the user in the Event monitoring. This operation can separate the registration of basic functions from the corresponding policy services and reduce the coupling of the system. In the future, expanding the registration service, such as adding risk control strategy, adding real name authentication and judging user attributes, will not affect the actions performed after successful registration.
Then in this chapter, we need to design and implement the container event and event listener function of Spring Event in the way of observer mode, so that we can finally define, monitor and publish our own event information in the existing Spring framework.

programme

In fact, the design of event itself is an implementation of observer mode. What it wants to solve is the problem of notifying other objects of the change of object state. In addition, it should consider ease of use and low coupling to ensure a high degree of cooperation.
In terms of function implementation, we need to define event classes, event listening and event publishing, and the functions of these classes need to be combined with Spring's AbstractApplicationContext#refresh() to facilitate the operation of event initialization and registering event listeners. The overall design structure is shown in the figure below:

  • In the whole process of function implementation, it is still necessary to add relevant event content to the user oriented application context AbstractApplicationContext, including initializing the event publisher, registering the event listener, and publishing the container refresh completion event.
  • Use the observer mode to define the event class, listening class and publishing class. At the same time, it also needs to complete the function of a broadcaster. When receiving the event push, analyze and deal with the events that are in line with the interest of the listening event receiver, that is, use isAssignableFrom to judge.
  • isAssignableFrom is similar to instanceof, but isAssignableFrom is used to judge the relationship between subclasses and parent classes, or the relationship between interface implementation classes and interfaces. By default, the ultimate parent class of all classes is Object. If the result of A.isAssignableFrom(B) is true, it proves that B can be converted into A, that is, A can be converted from B

The class relationship between container events and event listeners is shown in Figure 11-2

  • The whole class diagram above focuses on the implementation of event event definition, publishing and listening functions, and the registration and processing of relevant contents of events using AbstractApplicationContext#refresh.
  • In the process of implementation, the spring context package is mainly extended, and the implementation of events is also extended under this package. Of course, it can be seen that all the current implementation contents are still mainly IOC.
  • The ApplicationContext container inherits the event publishing function interface ApplicationEventPublisher and provides event listening function in the implementation class.
  • The applicationeventmulticast interface is a broadcaster that registers listeners and publishes events. It provides methods to add, remove and publish events.
  • Finally, publish the container closing event, which still needs to be extended to the AbstractApplicationContext#close method and implemented by the hook registered to the virtual machine

debug debugging

start

public void test_event() {
    //(1)
     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
     //(9)  
    applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "succeed!"));

    //(10)
    applicationContext.registerShutdownHook();
    }

(1)AbstractApplicationContext

 public void refresh() throws BeansException {
        //1 create BeanFactory and load BeanDefinition
        refreshBeanFactory();

        //2 get BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        //3 add ApplicationContextAwareProcessor to inherit the Bean object of ApplicationContextAware
        //Can sense the ApplicationContext to which they belong
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

        //3 before bean instantiation, execute beanfactoryprocessor (call the factory processor registered as bean in the context)
        invokeBeanFactoryPostProcessors(beanFactory);

        //4. Beanpostprocessor needs to perform the registration operation before other Bean objects are instantiated
        registerBeanPostProcessors(beanFactory);

        //5 instantiate the singleton Bean object in advance
        beanFactory.preInstantiateSingletons();

        //6 initialize event publisher
        initApplicationEventMulticaster();//(2)

        //7 register event listener
        registerListeners();  //(3)

        //8 publishing container refresh completion event
        finishRefresh(); //(5)

    }

(2) AbstractApplicationContext
Assign its broadcaster attribute applicationeventmulticast and add it to the singleton collection of beanFactory

private ApplicationEventMulticaster applicationEventMulticaster;
 private void initApplicationEventMulticaster(){
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
    }

(3)AbstractApplicationContext
Get all data from spring. Com through getBeansOfType method The event configuration Bean object loaded into XML.

 private void registerListeners(){
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener listener:applicationListeners){
            applicationEventMulticaster.addApplicationLister(listener);//(4)
        }
    }

(4)AbstractApplicationEventMulticaster
Add listening events to the applicationListeners collection

public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();
@Override
    public void addApplicationLister(ApplicationListener<?> listener) {
       applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

(5)AbstractApplicationContext
The event after the first server startup is completed is published. This event is published through publishEvent
Broadcast with applicationeventmulticast

//attribute
private ApplicationEventMulticaster applicationEventMulticaster;
 private void finishRefresh(){
        publishEvent(new ContextRefreshedEvent(this));
    }
@Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);//(6)
    }

(6)SimpleApplicationEventMulticaster
First filter out the qualified listener processor, and then send the message to the qualified listener

 @Override
    public void multicastEvent(final ApplicationEvent event) {
        for (final ApplicationListener listener:getApplicationListener(event)){ //(7)
            listener.onApplicationEvent(event); //(8)
        }
    }

(7) AbstractApplicationEventMulticaster
Extract the listening processor in accordance with the broadcast event. The specific filtering action is in the supportsEvent method.

 protected Collection<ApplicationListener> getApplicationListener(ApplicationEvent event){
        LinkedList<ApplicationListener> allListeners = new LinkedList<>();
        for (ApplicationListener<ApplicationEvent> listener :applicationListeners){
            if (supportsEvent(listener,event)){
                allListeners.add(listener);
            }
        }
        return allListeners;
    }

(8)ContextRefreshedEventListener
implements ApplicationListener
Call the refresh method in our own class that implements the ApplicationListener interface

  @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("Refresh event:"+this.getClass().getName());
    }

(9)AbstractApplicationContext

 public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }

In fact, the next step is step (6)
(10)AbstractApplicationContext

public void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    }

(11)AbstractApplicationContext

public void close() {
        //Publish container close event
        publishEvent(new ContextClosedEvent(this)); //(12)

        //Execute the destruction method of destroying the singleton bean
        getBeanFactory().destroySingletons();
    }

(12)AbstractApplicationContext

@Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }

In fact, the next step is step (6)

Keywords: Java Spring Container

Added by FUEL on Mon, 07 Feb 2022 02:29:39 +0200