SpringBoot啟動加載監(jiān)聽器以及監(jiān)聽?wèi)?yīng)用啟動階段事件

注:SpringBoot版本2.5.2

一、監(jiān)聽器的加載


public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null");
  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  this.webApplicationType = WebApplicationType.deduceFromClasspath();
  this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   //這里就用了SPI技術(shù)加載spring-factories定義的ApplicationListener實現(xiàn)類
   //然后把這些監(jiān)聽器保存到SpringApplication對象中
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = deduceMainApplicationClass();
}

這里以springboot的META-INFO下的spring-factories為例

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
image-20210722114716299.png

可以看到這里加載了10個定義在所有jar中的spring-factories文件中定義的ApplicationListener實現(xiàn)類。

二、廣播器方法的調(diào)度類SpringApplicationRunListener的加載和初始化

//SpringApplication
public ConfigurableApplicationContext run(String... args) {
    //.....
    //這里看到有加載SpringApplicationRunListeners對象的方法
    //該對象持有 SpringApplicationRunListener 對象的數(shù)組 ,從名字可知這個對象是驅(qū)動listner做一下操作的紫岩,主要定義了廣播事件的方法
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
     //.....
}
//SpringApplication
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
                    getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
                    this.applicationStartup);
}

我們看到有一個new SpringApplicationRunListeners的語句,里面有一個參數(shù)使用了getSpringFactoriesInstances方法獲取的,熟悉SpringBoot的朋友們應(yīng)該知道這里又是使用SPI的方式去spring-factories下面找到給定的接口類的實現(xiàn)類慰毅,然后把他們加載進來,這里給的指定接口類是SpringApplicationRunListener扎阶。

//new SpringApplicationRunListeners進入到該類的構(gòu)造器方法
class SpringApplicationRunListeners {
            //記錄日志的對象
            private final Log log;
            //SpringApplicationRunListener對象的存儲列表
            private final List<SpringApplicationRunListener> listeners;
            //啟動類對象
            private final ApplicationStartup applicationStartup;
            //SpringApplicationRunListeners 的構(gòu)造方法
            SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners,
                    ApplicationStartup applicationStartup) {
                this.log = log;
                this.listeners = new ArrayList<>(listeners);
                this.applicationStartup = applicationStartup;
        }
}

知道了SpringApplicationRunListener的實現(xiàn)類是采用了SPI的方式加載的汹胃,那我們就去參考SpringBoot的spring-factories文件,看一下文件中定義的SpringApplicationRunListener實現(xiàn)類有哪些

# RunListeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

可以看到這里只定義了一個EventPublishingRunListener

三东臀、廣播器的加載

上面指出了廣播器調(diào)度類的加載着饥,從設(shè)計模式的角度,一個調(diào)度類應(yīng)該是以屬性組合的關(guān)系持有這他調(diào)度的對象惰赋,這里的廣播器調(diào)度類也持有一個廣播器對象宰掉,我們看之前加載的廣播器調(diào)度類的結(jié)構(gòu),可以看到持有這一個SimpleApplicationEventMulticaster廣播器對象赁濒。并且在使用SPI加載的時候就默認(rèn)調(diào)用了唯一的構(gòu)造方法初始化了這個廣播器對象轨奄,以及這個廣播器的監(jiān)聽器列表。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
     //SpringApplication對象成員變量
     private final SpringApplication application;
     //啟動參數(shù)數(shù)組
     private final String[] args;
     //事件的廣播器
     private final SimpleApplicationEventMulticaster initialMulticaster;
     //構(gòu)造器
     public EventPublishingRunListener(SpringApplication application, String[] args) {
           this.application = application;
           this.args = args;
           this.initialMulticaster = new SimpleApplicationEventMulticaster();
           //把SpringApplication對象的構(gòu)造階段(SPI)加載的監(jiān)聽器列表listeners存儲到廣播器的監(jiān)聽器列表中
           for (ApplicationListener<?> listener : application.getListeners()) {
                this.initialMulticaster.addApplicationListener(listener);
           }
     }
}

從該類的定義可知
①這個對象持有SpringApplication對象拒炎,可以從這個對象中或的創(chuàng)建SpringApplication對象的時候加載的listener列表挪拟。
②持有廣播器SimpleApplicationEventMulticaster對象
③只定義了一個構(gòu)造方法,說明當(dāng)使用SPI加載該類的時候默認(rèn)調(diào)用的就是以上的方法击你,則這些參數(shù)就在加載進來的時候就已經(jīng)初始化玉组,例如監(jiān)聽器列表。

3.1廣播器的監(jiān)聽器存儲位置

看一下這些監(jiān)聽器被添加到了廣播器的哪里

this.initialMulticaster.addApplicationListener(listener);
//AbstractApplicationEventMulticaster
public abstract class AbstractApplicationEventMulticaster
        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
      //持有的默認(rèn)檢索器丁侄,存儲在new SpringApplication對象的時候使用SPI加載進來的監(jiān)聽器對象Listener列表
      private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
      //內(nèi)部類
      private class DefaultListenerRetriever {
          //存儲ApplicationListener對象集合的set
          public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
          //存儲聲明的ApplicationListener的bean的名稱集合的set
          public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
      }
      //在之前提到的this.initialMulticaster.addApplicationListener(listener);步驟調(diào)用的
      public void addApplicationListener(ApplicationListener<?> listener) {
            synchronized (this.defaultRetriever) {
                Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
                if (singletonTarget instanceof ApplicationListener) {
                  this.defaultRetriever.applicationListeners.remove(singletonTarget);
                 }
                this.defaultRetriever.applicationListeners.add(listener);
                 this.retrieverCache.clear();
      }
}

可以看到這些監(jiān)聽器都被添加進了廣播器的 defaultRetriever 對象屬性中惯雳。

到這里我們就知道SpringApplicationRunListeners對象已經(jīng)加載完了,而且明確了以下關(guān)系鸿摇;

SpringApplicationRunListeners對象-》持有EventPublishingRunListener數(shù)組-》 每一個EventPublishingRunListener都持有一個SimpleApplicationEventMulticaster廣播器對象-》這個廣播器持有DefaultListenerRetriever檢索器對象-》 這個檢索器對象持有SpringApplication創(chuàng)建的時候加載的監(jiān)聽器列表石景。

后續(xù)的發(fā)布事件操作將通過SpringApplicationRunListeners對象封裝的方法然后for循環(huán)調(diào)用每EventPublishingRunListener的對象持有的廣播器SimpleApplicationEventMulticaster對象進行事件發(fā)布。

四户辱、事件發(fā)布流程

從SpringApplication的run方法可以看到第一個應(yīng)用程序事件的發(fā)布 - 》 使用廣播器調(diào)度對象數(shù)組對象發(fā)布事件ApplicationStartingEvent

4.1 廣播器調(diào)度對象數(shù)組對象調(diào)用對應(yīng)的方法

//SpringApplication
public ConfigurableApplicationContext run(String... args) {
        ......
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        ......
}

4.2 廣播器調(diào)度對象數(shù)組對象循環(huán)調(diào)用數(shù)組里面的廣播器調(diào)度對象鸵钝,每個對象再調(diào)用事件對應(yīng)的方法

//SpringApplicationRunListeners

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
        doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
        // ...........
}

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
      Consumer<StartupStep> stepAction) {
              // ...........
              this.listeners.forEach(listenerAction);
             // ...........
}

4.3 廣播器調(diào)度器再調(diào)用自己持有的廣播器對象調(diào)用廣播方法發(fā)布事件

從上方的傳入?yún)?shù) Consumer<StartupStep> stepAction 可知,這里是分別調(diào)用了List<SpringApplicationRunListener> listeners數(shù)組中所有的SpringApplicationRunListener對象的starting方法

//EventPublishingRunListener
public void starting(ConfigurableBootstrapContext bootstrapContext) {
    //從第二點我們就知道每一個runListener對象都持有廣播器SimpleApplicationEventMulticaster對象,
    //同時這個eventMulticaste對象還持有DefaultListenerRetriever類對象應(yīng)用,而EventPublishingRunListener對象在創(chuàng)建的時候
    //就把DefaultListenerRetriever對象的listener數(shù)組初始化為SPI加載的所有l(wèi)istener集合
    this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}

先看ApplicationStartingEvent事件對象的創(chuàng)建方法(構(gòu)造器方法)

//ApplicationStartingEvent

public ApplicationStartingEvent(ConfigurableBootstrapContext bootstrapContext, SpringApplication application,
String[] args) {
      super(application, args);
      this.bootstrapContext = bootstrapContext;
}

從構(gòu)造方法可以看的出來,主要初始化三個數(shù)據(jù)
①設(shè)置加載上下文
②設(shè)置事件的來源,是從哪個對象創(chuàng)建的事件
③設(shè)置參數(shù)

五、監(jiān)聽器的篩選

再看廣播器SimpleApplicationEventMulticaster對象的multicastEvent方法

//SimpleApplicationEventMulticaster

public void multicastEvent(ApplicationEvent event) {
    //event就是事件對象,resolveDefaultEventType(event)返回的是對event進行包裝
    multicastEvent(event, resolveDefaultEventType(event));
}

image-20210722133559878.png

可以看到這個type是記錄了這個事件的類型,加載器,父類以及這個類的繼承結(jié)構(gòu)樹等信息

5.1 廣播器的廣播方法

//SimpleApplicationEventMulticaster

//該方法主要用于根據(jù)事件的類型,獲取到對這個事件感興趣的監(jiān)聽器,然后調(diào)用監(jiān)聽器的監(jiān)聽方法onApplication執(zhí)行對應(yīng)的事件響應(yīng)邏輯
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
      ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
      Executor executor = getTaskExecutor();
      //getApplicationListeners 獲取對這個事件感興趣的監(jiān)聽器
      for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                  executor.execute(() -> invokeListener(listener, event));
            }
            else {
                  //調(diào)用這些監(jiān)聽器的onApplication方法對這個事件做出響應(yīng)
                  invokeListener(listener, event);
            }
      }
}

5.2 根據(jù)事件類型篩選監(jiān)聽器的(getApplicationListeners)

5.2.1 根據(jù)事件類型加來源獲取存儲監(jiān)聽該事件的監(jiān)聽器的容器

可以看到下方方法中是使用了一個ConcurrentHashMap來存儲事件和監(jiān)聽器的關(guān)系的庐镐,避免每次都需要根據(jù)事件類型查詢對應(yīng)的監(jiān)聽器恩商,當(dāng)已經(jīng)在緩存中存在的關(guān)系,則直接根據(jù)事件來構(gòu)造key值必逆,從緩存中直接獲取監(jiān)聽器列表怠堪;否則就構(gòu)造一個存儲對應(yīng)的監(jiān)聽器容器對象存放到緩存中揽乱。

//AbstractApplicationEventMulticaster
/**
* Return a Collection of ApplicationListeners matching the given event type.
*/

private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
      final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
      protected Collection<ApplicationListener<?>> getApplicationListeners(
            ApplicationEvent event, ResolvableType eventType) {
              //首先先獲取事件的源 source,也就是 SpringApplication
              Object source = event.getSource();
              //獲得source的class type
              Class<?> sourceType = (source != null ? source.getClass() : null);
              //通過sourceType和eventType構(gòu)造一個緩存key
              //目的是若當(dāng)前已經(jīng)獲得過對當(dāng)前事件感興趣的監(jiān)聽器列表粟矿,則從緩存中讀取凰棉,
              //不必再重新進行計算哪些監(jiān)聽器對該事件感興趣,提升了效率
              ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
              // Potential new retriever to populate
              CachedListenerRetriever newRetriever = null;
              // Quick check for existing entry on ConcurrentHashMap
             CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
             //第一次調(diào)用的話retriever為null
            if (existingRetriever == null) {
            // Caching a new ListenerRetriever if possible
            //如果需要緩存一個新的ListenerRetriever(監(jiān)聽器檢索器)
            //這里以前是用鎖鎖住了陌粹,獲取listener列表和放入cache是在同步塊撒犀,但是這樣效率太低了。使用鎖應(yīng)該盡可能的使得鎖的粒度更低
            //所以相比于以前掏秩,這里進行了優(yōu)化或舞,不用鎖而是使用了普通的putIfAbsent方法,先做簡單的過濾掉大部分不爭用鎖的情況
            //再在需要使用鎖的時候在retrieveApplicationListeners進行小粒度的鎖
                  if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                      (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
                  
                      newRetriever = new CachedListenerRetriever();
                      //如果緩存中已存在蒙幻,則會返回緩存中的對象映凳,否則返回空
                      //注意-》這里只是存儲了用于存儲listener列表的對象-》對于未存入緩存的listener列表在
                      //下一步retrieveApplicationListeners處理
                      existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
                      if (existingRetriever != null) { 
                      //緩存中已經(jīng)有了數(shù)據(jù)就不需要填充進去了,當(dāng)cache中無數(shù)據(jù)的時候
                      //putIfAbsent會返回null邮破,有數(shù)據(jù)則會返回舊數(shù)據(jù)而不執(zhí)行put的操作
                            newRetriever = null; 
                      }
                  }
            }
            
            //緩存中retriever不為null诈豌,直接返回retriever的獲取監(jiān)聽器方法
            if (existingRetriever != null) {
                   Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
                   if (result != null) {
                        return result;
                   }
                  /**
                      這里會出現(xiàn)緩存中存在檢索器,但是監(jiān)聽器為空的情況是因為當(dāng)另一個線程
                      設(shè)置existingRetriever的時候被感知到了
                      另一個線程比當(dāng)前線程調(diào)用putIfAbsent方法快一點抒和,參考CurrentHashMap的特點矫渔,
                      所以這個線程可以感知到緩存中已經(jīng)存在existingRetriever了,
                      但是另一個線程也是執(zhí)行到這里构诚,所以并不執(zhí)行接下來的etrieveApplicationListeners初始化
                      檢索器持有的監(jiān)聽器蚌斩,而是稍后繼續(xù)執(zhí)行,所以導(dǎo)致這里獲取到的監(jiān)聽器還是空的铆惑,
                      所以這里的處理是當(dāng)前線程默認(rèn)已經(jīng)把監(jiān)聽器加載進去了范嘱,
                      在下一步方法中通過newRetriever為空      來判斷
                      是否對檢索器進行監(jiān)聽器列表的屬性的賦值,默認(rèn)是把已經(jīng)加載到SpringApplication對象
                      持有的監(jiān)聽器列表返回(因為另一個線程如果填充也是填充這些)
                **/
                //ConcurrentHashMap的參考鏈接:https://www.cnblogs.com/xiaoxi/p/7474026.html
                // If result is null, the existing retriever is not fully populated yet by another thread.
                // Proceed like caching wasn't possible for this current local attempt.
           }
        //No ListenerRetriever caching -> no synchronization necessary
       return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

5.3 監(jiān)聽器容器對象的初始化

當(dāng)上面的方法設(shè)置好存儲監(jiān)聽器的容器后员魏,就到了初始化這個檢索器設(shè)置事件和對該事件感興趣的監(jiān)聽器列表的方法了-》retrieveApplicationListeners方法丑蛤。

這個階段主要的問題就是根據(jù)廣播的事件的類型篩選出哪些監(jiān)聽器需要做出響應(yīng),然后把這些監(jiān)聽器以 事件類型加來源:對這個事件感興趣的監(jiān)聽器列表 的形式存儲在 concurrentHashMap 中撕阎。

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

            List<ApplicationListener<?>> allListeners = new ArrayList<>();
            Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
            Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
            
            Set<ApplicationListener<?>> listeners;
            Set<String> listenerBeans;
            synchronized (this.defaultRetriever) {
                  //這里defaultRetriever的監(jiān)聽器列表是在SpringApplication創(chuàng)建的時候初始化和賦值
                  listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
                  listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
            }
            // Add programmatically registered listeners, including ones coming
            // from ApplicationListenerDetector (singleton beans and inner beans).
            for (ApplicationListener<?> listener : listeners) {
                  //遍歷這些監(jiān)聽器受裹,看看那些監(jiān)聽器是歸這個事件感興趣的
                  if (supportsEvent(listener, eventType, sourceType)) {
                        if (retriever != null) {
                              filteredListeners.add(listener);
                        }
                  allListeners.add(listener);
                  }
            }
            // Add listeners by bean name, potentially overlapping with programmatically
            // registered listeners above - but here potentially with additional metadata.
            if (!listenerBeans.isEmpty()) {
                  ConfigurableBeanFactory beanFactory = getBeanFactory();
                  for (String listenerBeanName : listenerBeans) {
                        try {
                              if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                                 ApplicationListener<?> listener =
                                 beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                                 if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                                     if (retriever != null) {
                                         if (beanFactory.isSingleton(listenerBeanName)) {
                                             filteredListeners.add(listener);
                                         }
                                         else {
                                             filteredListenerBeans.add(listenerBeanName);
                                         }
                                    }
                                    allListeners.add(listener);
                                 }
                              }
                              else {
                                  // Remove non-matching listeners that originally came from
                                  // ApplicationListenerDetector, possibly ruled out by additional
                                  // BeanDefinition metadata (e.g. factory method generics) above.
                                  Object listener = beanFactory.getSingleton(listenerBeanName);
                                  if (retriever != null) {
                                      filteredListeners.remove(listener);
                                  }
                                  allListeners.remove(listener);
                              }
                          }catch (NoSuchBeanDefinitionException ex) {
                               // Singleton listener instance (without backing bean definition) disappeared -
                               // probably in the middle of the destruction phase
                          }
                  }
            }
            
            AnnotationAwareOrderComparator.sort(allListeners);
            if (retriever != null) {
                if (filteredListenerBeans.isEmpty()) {
                    retriever.applicationListeners = new LinkedHashSet<>(allListeners);
                    retriever.applicationListenerBeans = filteredListenerBeans;
                }
                else {
                    retriever.applicationListeners = filteredListeners;
                     retriever.applicationListenerBeans = filteredListenerBeans;
                }
            }
            
            return allListeners;
}

5.3.1 supportsEvent 判斷是否支持監(jiān)聽這個事件

觀察supportsEvent的內(nèi)部邏輯看看是如何判斷哪些監(jiān)聽器對這個事件感興趣

//AbstractApplicationEventMulticaster
protected boolean supportsEvent(
      ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
      //由于supportsEventType方法只支持GenericApplicationListener類型的監(jiān)聽器,所以先使用設(shè)配器
      //抽取出監(jiān)聽器的聲明的指定監(jiān)聽的問題的類型以及監(jiān)聽器的對象虏束,創(chuàng)建一個普通的監(jiān)聽器
      //對象 GenericApplicationListener棉饶,便于后續(xù)對這些不同的監(jiān)聽器采取一樣的判斷操作
      GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
            (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
      return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

image-20210726113024068.png
image-20210726113045359.png

在調(diào)用適配器轉(zhuǎn)換為GenericApplicationListenerAdapter(繼承了GenericApplicationListener)對象后,通過這個對象調(diào)用supportsEventType方法和supportsSourceType方法

//GenericApplicationListenerAdapter

public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
      Assert.notNull(delegate, "Delegate listener must not be null");
      //委托適配的監(jiān)聽器對象
      this.delegate = (ApplicationListener<ApplicationEvent>) delegate;
      //解析該監(jiān)聽器感興趣的事件類型
      //declaredEventType 這個監(jiān)聽器的泛型指定的類型(聲明的類型是)
      this.declaredEventType = resolveDeclaredEventType(this.delegate);
}

//解析該監(jiān)聽器感興趣的事件類型
private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
      ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
      if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) {
            Class<?> targetClass = AopUtils.getTargetClass(listener);
            if (targetClass != listener.getClass()) {
                  declaredEventType = resolveDeclaredEventType(targetClass);
            }
      }
      return declaredEventType;
}

static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {
        ResolvableType eventType = eventTypeCache.get(listenerType);
        if (eventType == null) {
            eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric();
            eventTypeCache.put(listenerType, eventType);
        }
        return (eventType != ResolvableType.NONE ? eventType : null);
}

調(diào)用設(shè)配器轉(zhuǎn)換完后可以知道這個監(jiān)聽器的監(jiān)聽事件類型镇匀,以及監(jiān)聽器對象

image-20210726122029442.png

接下來就是根據(jù)適配后的對象調(diào)用方法supportsEventType和supportsSourceType判斷這個監(jiān)聽器對該事件感興趣與否照藻。

public boolean supportsEventType(ResolvableType eventType) {
         //根據(jù)這個方法的調(diào)用方判斷這個對象的類別(通過之前的Adapter適配后設(shè)置或者原本就有的delegate來判斷)
        if (this.delegate instanceof GenericApplicationListener) {
            //為真則調(diào)用這個對象的supportsEventType
            return ((GenericApplicationListener) this.delegate).supportsEventType(eventType);
        }
        else if (this.delegate instanceof SmartApplicationListener) {
            Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
            return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
        }
        else {
            //不為真則根據(jù)這個監(jiān)聽器監(jiān)聽的事件類型declaredEventType和傳進來的時間類型eventType相匹配,返回這個判斷結(jié)果
            //declaredEventType為空則說明這個監(jiān)聽器沒有設(shè)置監(jiān)聽的事件類型汗侵,則監(jiān)聽所有的事件幸缕,返回true
            //不為空則對比聲明的監(jiān)聽類型是否和傳遞進來的事件類型是否一致
            return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
        }
}

這里有一個疑問為啥要單獨分開supportsEventType判斷群发,最后分支的else不適配特定類型的監(jiān)聽器?
當(dāng)smartListener.supportsEventType(eventType)為真的時候則進行事件來源的判斷

//GenericApplicationListenerAdapter

public boolean supportsSourceType(@Nullable Class<?> sourceType) {
         //假如這個不是SmartApplicationListener的子類(GenericApplicationListener是SmartApplicationListener的子類)則為true发乔,
         //而如果是屬于SmartApplicationListener的子類則再調(diào)用SmartApplicationListener對象的supportsSourceType方法
        return !(this.delegate instanceof SmartApplicationListener) ||
                ((SmartApplicationListener) this.delegate).supportsSourceType(sourceType);
}

當(dāng)檢測出這個監(jiān)聽器對這個事件有監(jiān)聽的話就添加進監(jiān)聽器列表

//AbstractApplicationEventMulticaster

for (ApplicationListener<?> listener : listeners) {
            if (supportsEvent(listener, eventType, sourceType)) {
                if (retriever != null) {
                    filteredListeners.add(listener);
                }
                allListeners.add(listener);
            }
}

最后按照事件類型加來源 - 》 監(jiān)聽器列表放到concurrentHashMap中緩存起來

    AnnotationAwareOrderComparator.sort(allListeners);
    if (retriever != null) {
        if (filteredListenerBeans.isEmpty()) {
            retriever.applicationListeners = new LinkedHashSet<>(allListeners);
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
        else {
            retriever.applicationListeners = filteredListeners;
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
    }
    return allListeners;

六熟妓、監(jiān)聽器對事件做出響應(yīng)

我們回到廣播器的廣播方法

//SimpleApplicationEventMulticaster

//該方法主要用于根據(jù)事件的類型,獲取到對這個事件感興趣的監(jiān)聽器,然后調(diào)用監(jiān)聽器的監(jiān)聽方法onApplication執(zhí)行對應(yīng)的事件響應(yīng)邏輯
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
      ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
      Executor executor = getTaskExecutor();
      //getApplicationListeners 獲取對這個事件感興趣的監(jiān)聽器
      for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                  executor.execute(() -> invokeListener(listener, event));
            }
            else {
                  //調(diào)用這些監(jiān)聽器的onApplication方法對這個事件做出響應(yīng)
                  invokeListener(listener, event);
            }
      }
}

我們通過以上的方法已經(jīng)找到了對這個事件感興趣的監(jiān)聽器列表,之后就是遍歷這些監(jiān)聽器栏尚,然后分別調(diào)用這些監(jiān)聽器的對應(yīng)的響應(yīng)的方法onApplication

我們看invokerListener方法

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    //調(diào)用這些監(jiān)聽器的onApplication方法對這個事件做出響應(yīng)
    invokeListener(listener, event);
}

這個方法傳入了需要調(diào)用的監(jiān)聽器起愈,以及事件對象

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            doInvokeListener(listener, event);
        }
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
                    (event instanceof PayloadApplicationEvent &&
                            matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                // -> let's suppress the exception.
                Log loggerToUse = this.lazyLogger;
                if (loggerToUse == null) {
                    loggerToUse = LogFactory.getLog(getClass());
                    this.lazyLogger = loggerToUse;
                }
                if (loggerToUse.isTraceEnabled()) {
                    loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
}

我們可以看到 listener.onApplicationEvent(event) 是主動調(diào)用了這個監(jiān)聽器的響應(yīng)方法的。

七译仗、總結(jié)

通過該監(jiān)聽器的加載流程中了解到SPI的加載這種方式的便利性告材,簡化了xml配置,為注解編程提供了切入點古劲;同時從廣播器調(diào)度器這種設(shè)計思想斥赋,通過封裝廣播方法從而簡化調(diào)用者發(fā)布事件,降低耦合度产艾,以及適配器的使用疤剑,SpringBoot中的事件發(fā)布機制。

八闷堡、參考鏈接

1隘膘、http://redmapleren.com/2020/01/17/%E8%B0%88%E8%B0%88SpringBoot%E7%9A%84%E7%9B%91%E5%90%AC%E5%99%A8/
2、
https://www.cnblogs.com/xiaoxi/p/7474026.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載杠览,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者弯菊。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市踱阿,隨后出現(xiàn)的幾起案子管钳,更是在濱河造成了極大的恐慌,老刑警劉巖软舌,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件才漆,死亡現(xiàn)場離奇詭異,居然都是意外死亡佛点,警方通過查閱死者的電腦和手機醇滥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來超营,“玉大人鸳玩,你說我怎么就攤上這事⊙荼眨” “怎么了不跟?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長船响。 經(jīng)常有香客問我躬拢,道長躲履,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任聊闯,我火速辦了婚禮工猜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菱蔬。我一直安慰自己篷帅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布拴泌。 她就那樣靜靜地躺著魏身,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚪腐。 梳的紋絲不亂的頭發(fā)上箭昵,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音回季,去河邊找鬼家制。 笑死,一個胖子當(dāng)著我的面吹牛泡一,可吹牛的內(nèi)容都是我干的颤殴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鼻忠,長吁一口氣:“原來是場噩夢啊……” “哼涵但!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起帖蔓,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矮瘟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后讨阻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芥永,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡篡殷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年钝吮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片板辽。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡奇瘦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出劲弦,到底是詐尸還是另有隱情耳标,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布邑跪,位于F島的核電站次坡,受9級特大地震影響呼猪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砸琅,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一宋距、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧症脂,春花似錦谚赎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棕所,卻和暖如春闸盔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背琳省。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工蕾殴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岛啸。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓被因,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芭逝。 傳聞我的和親對象是個殘疾皇子毡惜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容