ApplicationListener原理分析

Nacos配置服務原理 文中結束時提到過經過發佈 ApplicationListener 刷新事件完成 Context 中屬性值的更新。那麼本章咱們一塊兒分析 ApplicationListener 原理。在開啓 ApplicationListener 解析以前咱們先看一個傳說中模式----觀察者模式(Observer)。html

觀察者模式

觀察者模式定義:對象間一種一對多的依賴關係,當一個被觀察的對象改變狀態,則會自動通知它的依賴對象。觀察者模式屬於行爲型模式。這是比較概念性的定義,下面我用一種接近生活的例子來詮釋觀察者模式。上大學的時候,不少學生常常曠課,可是快到期末考試那兩三節課基本是全到的,爲何呢?不錯,老師會劃考試重點!!!這時老師就是被學生觀察的對象,學生就是觀察者。當老師說如下這個知識點考試會考時,下面刷刷刷響起來,同窗們都在用筆畫標記!固然不一樣的學生用的辦法不同,好比學霸會用五光十色的表把重中之重區分出來,學渣可能就無論全用2B鉛筆畫(我就是這學渣中的其一)。看一下老師和學生的關係圖:java

觀察者模式

鄭重聲明一下,本學渣比較懶,沒有本身實現一個觀察者模式,直接用的是jdk提供的Observer和Obervable。接下來直接用代碼說明一切。緩存

被觀察對象

/**
 * 被觀察對象
 * @author Greiz
 */
public class TeacherObservable extends Observable {
    private String examKeyPoints;
    public String getExamKeyPoints() {
        return examKeyPoints;
    }
    public void setExamKeyPoints(String examKeyPoints) {
        this.examKeyPoints = examKeyPoints;
        // 修改狀態
        super.setChanged();
        // 通知全部觀察者
        super.notifyObservers(examKeyPoints);
    }
}

被觀察對象(老師),繼承了Observable。當變量考試重點(examKeyPoints)變了通知觀察者app

public class Observable {
    private boolean changed = false;
    private Vector<Observer> 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;
    }  
}

JDK中的Observable類。成員變量維護一個觀察者的列表,通知的時候遍歷該列表逐個調用update()方法。哈哈,是否是很簡單。分佈式

觀察者

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

監聽者用的也是JDK的,這傢伙比我還懶,就一個方法的接口,要乾的活都交給後代處理。ide

/**
 * @author Greiz
 */
public class ExcellentStudentObserver implements Observer {
    public static final String STUDENT = "我學霸";
    @Override
    public void update(Observable o, Object arg) {
        System.out.println(STUDENT + "用各類顏色的筆劃重點:" + arg.toString());
    }
}

監聽者 -- 學霸,當TeacherObservable成員變量改變時最終會調該類的update()方法。this

/**
 * @author Greiz
 */
public class PoorStudentObserver implements Observer {
    public static final String STUDENT = "我學渣一枚";
    @Override
    public void update(Observable o, Object arg) {
        System.out.println(STUDENT + "用2B鉛筆劃重點:" + arg.toString());
    }
}

監聽者 -- 學渣(我),當TeacherObservable成員變量改變時最終會調該類的update()方法。spa

管理者

老師和學生都有了,剩下的就差把他們聯繫起來了,總不能隨手一把抓吧,專業不對口劃重點也沒有啊!debug

/**
 * @author Greiz
 */
public class ObserverManager {
    public static void main(String[] args) {
        TeacherObservable observable = new TeacherObservable();
        // 給被觀察者對象添加觀察者
        observable.addObserver(new PoorStudentObserver());
        observable.addObserver(new ExcellentStudentObserver());
        // 修改被觀察者
        observable.setExamKeyPoints("這是考試重點!!!");
    }
}

一個簡單的觀察者模式完整的列子完成了。3d

小結

優勢

  1. 被觀察對象和觀察者之間解耦。

  2. 創建回調機制模型。

缺點

  1. 若是被觀察對象維護的觀察者列表中成員過多,遍歷通知會耗時至關長。 2. 若是被觀察對象和觀察者之間出現相互調用容易造成死循環。 3. 觀察者不清楚被觀察者對象變化的細節 4. 只能本地,不能分佈式。

ApplicationListener 源碼解析

ApplicationListener 跟上面觀察者模式有什麼關係呢?咱們先看源碼,後面一塊兒分析一下他們的關係。這節分兩個階段,一個調用階段,另外一個組裝階段。

調用階段

下面我畫出調用過程一些重要接口調用時序圖。

listener調用過程

源碼解析調用階段都是圍繞這個圖步驟進行。

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

自定義事件,須要繼承 ApplicationEvent。

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

添加自定義事件監聽者,必須加入Spring容器管理相關注解如 @Component,不然不起做用。

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

啓動Spring,發佈自定義事件

接下來進入時序圖中1-6 接口。

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

對應時序圖方法1, AbstractApplicationContext.publishEvent()。publishEvent方法是在 ApplicationEventPublisher 定義的,ApplicationEventPublisher 能夠理解成事件發射器。會調用 getApplicationEventMulticaster()

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

對應時序圖方法2,AbstractApplicationContext.getApplicationEventMulticaster()獲取事件廣播者。applicationEventMulticaster 在Spring啓動refresh過程 調用 initApplicationEventMulticaster() 初始化,是 SimpleApplicationEventMulticaster 實例。

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

對應時序圖方法3,SimpleApplicationEventMulticaster.multicastEvent()。根據事件類型,獲取全部對應的監聽者,而後遍歷通知(俗稱廣播)。

protected Collection<ApplicationListener<?>> 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<ApplicationListener<?>> listeners =
               retrieveApplicationListeners(eventType, sourceType, retriever);
         this.retrieverCache.put(cacheKey, retriever);
         return listeners;
      }
   }
   else {
      return retrieveApplicationListeners(eventType, sourceType, null);
   }
}

對應時序圖方法4,AbstractApplicationEventMulticaster.getApplicationListeners()。根據事件類型,先查詢緩存,若是緩存中沒有調用 retrieveApplicationListeners() 獲取,而後存到緩存中。

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

   List<ApplicationListener<?>> allListeners = new ArrayList<>();
   Set<ApplicationListener<?>> listeners;
   Set<String> 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;
}

對應時序圖方法5,AbstractApplicationEventMulticaster.retrieveApplicationListeners()。 全部事件監聽者都在this.defaultRetriever 對象中,該對象的值初始化過程咱們在下一節分析。返回過濾後符合本次事件的監聽者。接下來咱們回到 時序圖方法3 中繼續調用 SimpleApplicationEventMulticaster.invokeListener()。

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			... 省略代碼
		}
	}

對應時序圖方法6,SimpleApplicationEventMulticaster.doInvokeListener()。 這裏就是真正調用監聽者的方法。

前面提出 ApplicationListener 跟觀察者模式有什麼關係呢?分析 publishEvent(...) 到 onApplicationEvent(...) 調用,是否是很像前面觀察者模式列子中 setExamKeyPoints() --> notifyObservers() --> update()。這一個階段能夠看做就是觀察者模式中調用階段。接下來咱們繼續分析觀察者和被觀察者對象綁定過程---組裝階段。

組裝階段

照舊,以圖開篇,接下來全靠編!!!

listener初始化過程

在調用階段時序圖5中得知符合對應事件的監聽者是從 AbstractApplicationEventMulticaster 成員 (ListenerRetriever)defaultRetriever 的 applicationListeners 和 applicationListenerBeans 屬性獲取的。組裝階段就是解析 defaultRetriever 初始化負值過程。

applicationListenerBeans 初始化負值過程:

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      ... ... 省略代碼
      try {
         ... ... 省略代碼
         // ListenerRetriever applicationListenerBeans 初始化負值過程在這裏面
         registerListeners();

         // ListenerRetriever applicationListeners 初始化負值過程在這裏面
         finishBeanFactoryInitialization(beanFactory);
         ... ... 省略代碼
      }

      catch (BeansException ex) {
      	... 省略代碼
      }
      finally {
      	... 省略代碼
      }
   }
}

對應時序圖方法1,AbstractApplicationContext.refresh()。省略了一下與本次目的無關的代碼。看註釋就好,哈哈。

protected void registerListeners() {
   // 初始化階段getApplicationListeners()返回空列表
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
   }
	 // 根據bean類型獲取
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }
  ... 省略代碼
}

對應時序圖方法2,AbstractApplicationContext.registerListeners()。注意看註釋,看註釋,看註釋!!!此處根據bean類型獲取,反應了前面 「添加自定義事件監聽者,必須加入Spring容器管理相關注解如 @Component,不然不起做用」的說法。

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

對應時序圖方法3,AbstractApplicationEventMulticaster.addApplicationListenerBean()。

AbstractApplicationEventMulticaster 成員 (ListenerRetriever)defaultRetriever 的applicationListenerBeans 屬性負值完成。

applicationListeners 初始化負值過程:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		... 省略代碼
		beanFactory.preInstantiateSingletons();
	}

對應時序圖方法4,AbstractApplicationContext.finishBeanFactoryInitialization()。

偷懶一次,跟着這個方法debug下去,最終調用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);
}

對應時序圖方法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();
   }
}

對應時序圖方法15,AbstractApplicationEventMulticaster.addApplicationListener()。恩,是否是很熟悉的defaultRetriever。

AbstractApplicationEventMulticaster 成員 (ListenerRetriever)defaultRetriever 的applicationListeners 屬性負值完成。

總結

Spring的事件監聽者模型能夠看做是觀察者模式,但Spring對JDK的觀察者模式作了擴展,根據事件類型廣播給對應的監聽者。其實不少Spring源碼都是介於JDK的基礎上作的擴展,若是把JDK比做生活,那麼Spring就是詩人。好詩都是來自生活而高於生活!

相關文章
相關標籤/搜索