Principle analysis of applicationlistener

Time:2020-2-15

At the end of Nacos configuration service principle article, it is mentioned that the update of property values in context is completed by publishing the applicationlistener refresh event. In this chapter, we will analyze the principle of applicationlistener. Before we turn on ApplicationListener parsing, let ‘s look at a legendary pattern – Observer.

Observer mode

Observer pattern definition: a one to many dependency between objects. When an observed object changes state, its dependent objects will be notified automatically. The observer pattern belongs to behavioral pattern. This is a more conceptual definition. Let me use an example close to life to explain the observer mode. In college, many students often cut classes, but nearly to the final exam that two or three classes are basically all arrived, why? Yes, the teacher will draw the key points of the exam!!! At this time, the teacher is the object observed by the students, and the students are the observers. When the teacher said that the following knowledge points will be tested, the next brush will ring, and the students are marking with strokes! Of course, different students use different methods. For example, Xueba will use a colorful watch to distinguish the most important points. Xueba may not use 2B pencil to draw all of them (I am one of them). Take a look at the relationship between teachers and students:

观察者模式

Solemnly, this learning slag is lazy. It doesn’t implement an observer mode by itself. It uses the observer and observable provided by JDK directly. Next, I’ll show you everything in code.

Observed object

/**
 *Observed object
 * @author Greiz
 */
public class TeacherObservable extends Observable {
    private String examKeyPoints;
    public String getExamKeyPoints() {
        return examKeyPoints;
    }
    public void setExamKeyPoints(String examKeyPoints) {
        this.examKeyPoints = examKeyPoints;
        //Modification status
        super.setChanged();
        //Notify all observers
        super.notifyObservers(examKeyPoints);
    }
}

The observed object (teacher) inherits the observable. Notify the observer when the variable exam points change

public class Observable {
    private boolean changed = false;
    private Vector obs;
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    public void notifyObservers() {
        notifyObservers(null);
    }
    public void notifyObservers(Object arg) {
        Object[] arrLocal;
        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    protected synchronized void setChanged() {
        changed = true;
    }  
}

The observable class in JDK. The member variable maintains a list of observers, and calls the update () method one by one when traversing the list. Ha ha, isn’t it very simple.

Observer

public interface Observer {
    void update(Observable o, Object arg);
}

The listener also uses JDK. This guy is lazier than me. He has to give the future generations the interface of a method and all the work to be done.

/**
 * @author Greiz
 */
public class ExcellentStudentObserver implements Observer {
    Public static final string study = "I study hegemony";
    @Override
    public void update(Observable o, Object arg) {
        System. Out. Println (study + "stroke emphasis with various colors:" + arg. Tostring());
    }
}

Listener — Xueba, when the teacherobservable member variable changes, the update() method of this class will be called finally.

/**
 * @author Greiz
 */
public class PoorStudentObserver implements Observer {
    Public static final string study = "I learn a piece of slag";
    @Override
    public void update(Observable o, Object arg) {
        System. Out. Println (study + "point with 2B pencil:" + arg. Tostring());
    }
}

Listener — xuezha (me), when the member variable of the teacherobservable changes, the update () method of the class will be called finally.

Controller

Teachers and students have, the rest of them are just about to link them up, we can’t grasp it at any time, there is no point in the profession!

/**
 * @author Greiz
 */
public class ObserverManager {
    public static void main(String[] args) {
        TeacherObservable observable = new TeacherObservable();
        //Add observers to the observed objects
        observable.addObserver(new PoorStudentObserver());
        observable.addObserver(new ExcellentStudentObserver());
        //Modify the observed
        Observable. Setexamkeypoints ("this is the key point of the exam!!! "";
    }
}

A simple observer pattern is complete with columns.

Summary

Advantage

  1. Decoupling between the observed object and the observer.

  2. Build the callback mechanism model.

shortcoming

  1. If there are too many members in the observer list maintained by the observed object, traversal notification will take a long time.
    2. If there are mutual calls between the observed object and the observer, it is easy to form a dead cycle.
    3. The observer is not clear about the details of the changes of the observed object
    4. It can only be local, not distributed.

Source code analysis of applicationlistener

What does applicationlistener have to do with the observer pattern above? Let’s first look at the source code, and then analyze their relationship. This section is divided into two phases, one is the call phase, the other is the assembly phase.

Calling phase

Next, I draw some important interface call sequence diagrams of the call procedure.

listener调用过程

The source code parsing call phase is all around this diagram step.

public class GreizEvent extends ApplicationEvent {
    public GreizEvent(Object source) {
        super(source);
    }
    private String name = "Greiz";
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

Custom events, need to inherit applicationevent.

@Component
public class GreizListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(GreizEvent event) {
        System.out.println("=============" + event.getName());
    }
}

To add a custom event listener, you must add a spring container management related annotation such as @ component, otherwise it will not work.

public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext("com.greiz.demo.listener");
    context.publishEvent(new GreizEvent("Greiz"));
}

Start spring and publish custom events

Next, enter the 1-6 interface in the sequence diagram.

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
   ApplicationEvent applicationEvent;
   if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
   }
   ... omit code
   if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
   }
   else {
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
   }
   ... omit code
}

Corresponding to the sequence diagram method 1, abstractapplicationcontext. Publishevent(). The publishevent method is defined in applicationeventpublisher, which can be understood as an event emitter. Getapplicationeventmulticaster() will be called

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;
}

Corresponding to the sequence diagram method 2, abstractapplicationcontext. Getapplicationeventmulticaster() gets the event broadcaster. The applicationeventmulticasting starts the refresh procedure call initapplicationeventmulticasting() in spring to initialize. It is a simpleapplicationeventmulticasting instance.

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   for (ApplicationListener> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

Corresponding to sequence diagram method 3, simpleapplicationeventmulticaster. Multicasevent(). According to the event type, get all the corresponding listeners, and then traverse the notification (commonly known as broadcast).

protected Collection> getApplicationListeners(
      ApplicationEvent event, ResolvableType eventType) {
   Object source = event.getSource();
   Class> sourceType = (source != null ? source.getClass() : null);
   ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
   ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
   if (retriever != null) {
      return retriever.getApplicationListeners();
   }

   if (this.beanClassLoader == null ||
         (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
               (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
      synchronized (this.retrievalMutex) {
         retriever = this.retrieverCache.get(cacheKey);
         if (retriever != null) {
            return retriever.getApplicationListeners();
         }
         retriever = new ListenerRetriever(true);
         Collection> listeners =
               retrieveApplicationListeners(eventType, sourceType, retriever);
         this.retrieverCache.put(cacheKey, retriever);
         return listeners;
      }
   }
   else {
      return retrieveApplicationListeners(eventType, sourceType, null);
   }
}

Corresponding to the sequence diagram method 4, abstractapplicationeventmulticaster. Getapplicationlisteners(). According to the event type, query the cache first. If retrieveapplicationlisteners() is not called in the cache, it will be saved in the cache.

private Collection> retrieveApplicationListeners(
      ResolvableType eventType, @Nullable Class> sourceType, @Nullable ListenerRetriever retriever) {

   List> allListeners = new ArrayList<>();
   Set> listeners;
   Set listenerBeans;
   synchronized (this.retrievalMutex) {
      listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
      listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
   }
   for (ApplicationListener listener : listeners) {
      if (supportsEvent(listener, eventType, sourceType)) {
         if (retriever != null) {
            retriever.applicationListeners.add(listener);
         }
         allListeners.add(listener);
      }
   }
   if (!listenerBeans.isEmpty()) {
      BeanFactory beanFactory = getBeanFactory();
      for (String listenerBeanName : listenerBeans) {
         try {
            Class> listenerType = beanFactory.getType(listenerBeanName);
            if (listenerType == null || supportsEvent(listenerType, eventType)) {
               ApplicationListener> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
               if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                  if (retriever != null) {
                     if (beanFactory.isSingleton(listenerBeanName)) {
                        retriever.applicationListeners.add(listener);
                     }
                     else {
                        retriever.applicationListenerBeans.add(listenerBeanName);
                     }
                  }
                  allListeners.add(listener);
               }
            }
         }
         catch (NoSuchBeanDefinitionException ex) {
         }
      }
   }
   AnnotationAwareOrderComparator.sort(allListeners);
   if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
      retriever.applicationListeners.clear();
      retriever.applicationListeners.addAll(allListeners);
   }
   return allListeners;
}

Corresponding to the sequence diagram method 5, abstractapplicationeventmulticaster. Retrieveapplicationlisteners(). All event listeners are in this.defaultretriever object. We will analyze the value initialization process of this object in the next section. Returns the listener that matches this event after filtering. Next, we go back to the sequence diagram method 3 and call simpleapplicationeventmulticaster. Invokelistener().

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            ... omit code
        }
    }

Corresponding to sequence diagram method 6, simpleapplicationeventmulticaster. Doinvokelistener(). Here is the method that actually calls the listener.

What’s the relationship between applicationlistener and observer pattern? Analyze whether the call from publishevent (…) to onapplicationevent (…) is very similar to setexamkeypoints() — notifyobservers() — update() in the previous observer mode column. This stage can be regarded as the invocation stage in the observer mode. Next, we will continue to analyze the binding process between the observer and the observed object — the assembly phase.

Assembly stage

As usual, to start with the picture, next all depends on editing!!!

listener初始化过程

In the call phase sequence diagram 5, it is known that the listener matching the corresponding event is obtained from the applicationlisteners and applicationlistenerbeans properties of the defaultretriever of the abstractapplicationevent multicaster member (listenerretriever). The assembly phase is the process of parsing defaultretriever to initialize negative values.

Applicationlistenerbeans initializes the negative value process:

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      ... omit code
      try {
         ... omit code
         //Listener retriever applicationlistenerbeans initializes the negative value process here
         registerListeners();

         //Listener retriever applicationlisteners initializes the negative value process here
         finishBeanFactoryInitialization(beanFactory);
         ... omit code
      }

      catch (BeansException ex) {
        ... omit code
      }
      finally {
        ... omit code
      }
   }
}

Corresponding to the sequence diagram method 1, abstractapplicationcontext. Refresh(). Omit the code irrelevant to this purpose. Just read the notes, ha ha.

protected void registerListeners() {
   //The initialization phase getapplicationlisteners() returns an empty list
   for (ApplicationListener> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
   }
     //Get according to bean type
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }
  ... omit code
}

Corresponding to sequence diagram method 2, abstractapplicationcontext. Registerlisteners(). Pay attention to the notes, look at the notes, look at the notes!!! This is obtained according to the bean type, reflecting the previous saying that “to add a custom event listener, you must add spring container management related annotation such as @ component, otherwise it will not work”.

public void addApplicationListenerBean(String listenerBeanName) {
   synchronized (this.retrievalMutex) {
      this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
      this.retrieverCache.clear();
   }
}

Corresponding to the sequence diagram method 3, abstractapplicationeventmulticaster. Addapplicationlistenerbean().

Negative value of applicationlistenerbeans property of abstractapplicationeventmulticaster member (listenerretriever) defaultretriever completed.

Applicationlisteners initializes the negative value process:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        ... omit code
        beanFactory.preInstantiateSingletons();
    }

Corresponding to the sequence diagram method 4, abstractapplicationcontext. Finishbeanfactoryinitialization().

Lazy once, follow this method to debug, and finally call abstractapplicationcontext. Addapplicationlistener().

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);
}

Corresponding to sequence diagram method 14, abstractapplicationcontext. Addapplicationlistener().

public void addApplicationListener(ApplicationListener> listener) {
   synchronized (this.retrievalMutex) {
      Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
      if (singletonTarget instanceof ApplicationListener) {
         this.defaultRetriever.applicationListeners.remove(singletonTarget);
      }
      this.defaultRetriever.applicationListeners.add(listener);
      this.retrieverCache.clear();
   }
}

Corresponding to the sequence diagram method 15, abstractapplicationeventmulticaster. Addapplicationlistener(). Well, is it the familiar defaultretriever.

Negative value of applicationlisteners property of abstractapplicationeventmulticaster member (listenerretriever) defaultretriever completed.

summary

Spring’s event listener model can be regarded as the observer pattern, but spring has extended the observer pattern of JDK and broadcast it to the corresponding listener according to the event type. In fact, many spring source codes are extensions based on JDK. If you compare JDK to life, then spring is a poet. Good poetry comes from life and is higher than life!