springboot源碼分析(五)-監聽器實現原理(中)


本文是承接上一篇寫的:springboot源碼分析(四)-監聽器實現原理(上)

開篇之前先把祖師爺搬出來
  費玉清:問大家一個腦筋急轉彎,高個子男生,打一種甜品,很常見的那種
      思考。。。
      思考。。。
      思考。。。
  揭曉謎底: 蛋糕
  反正謎底我已經揭曉了,至於大家能不能看到,我就不管了,哈哈
 

概述

  上一篇文章已經把監聽器設計模式介紹一下,不太懂的可以先看一下上一篇文章,有助於理解本文,那本文就從源碼角度分析springboot中監聽器實現原理。

 

spring中的監聽器都是實現了如下兩個接口中的其中的一個

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    boolean supportsEventType(Class<? extends ApplicationEvent> var1);

    default boolean supportsSourceType(@Nullable Class<?> sourceType) {
        return true;
    }

    default int getOrder() {
        return 2147483647;
    }
}

說一下這兩個接口的區別:第一個接口是所有的監聽器都要實現的,里面就一個抽象方法,是一個函數式接口,第二個接口相比於第一個接口,擴展了幾個方法,其中

supportsEventType(Class<? extends ApplicationEvent> var1)

方法跟這個監聽器支持什么事件有關系,一會源碼的時候就知道這個方法的作用了

 

首先看一下系統監聽器初始化過程,和系統初始化器一樣都是放在一個列表中。

SpringApplication初始化函數如下:

 
         
private List<ApplicationListener<?>> listeners;
 
         
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList(listeners);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //這一行就是初始化監聽器的
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

進入this.getSpringFactoriesInstances(ApplicationListener.class)方法

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        //從META-INF/spring-factories中加載監聽器
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
     //實例化監聽器
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        //排序監聽器
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

看過我前幾篇系統初始化器的朋友應該對這幾個方法都不陌生,因為系統初始化器也是調用的這個方法,當時詳細了解析了這幾個方法的源碼,在這里就不重復了,感興趣的朋友可以翻翻我的前幾篇文章,都是springboot源碼系列的。

 

看過上篇文章的朋友都知道,監聽器模式需要幾個條件。

  • 目標
  • 具體目標
  • 監聽器
  • 事件(其實事件就是目標中發生了什么事,不同事對應不同的事件)
  • 觸發場景(這個上一篇文章中Test中,其實目標中執行了什么東西,然后觀察者才可以觀察到變化,這個其實就是觸發場景)

下面就從上面的幾個角度來分析

 

抽象目標

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {

    //存放監聽器的地方
  private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false);

    //新增監聽器
  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();
        }
    }
  //移除監聽器
  public void removeApplicationListener(ApplicationListener<?> listener) {
        synchronized(this.retrievalMutex) {
            this.defaultRetriever.applicationListeners.remove(listener);
            this.retrieverCache.clear();
        }
    }

  //省略N個其他方法
  

}

可以發現其實這個和我們上一節講的那個差不多,就是有幾個存放監聽器的列表,並且實現新增或者刪除監聽器的方法,但是這里沒有廣播后者叫着通知的方法,就是通知監聽器執行的方法,這個方法在具體的目標中實現。

 

具體目標

//這里可以發現繼承了抽象目標
public
class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {   
  //這個方法就是廣播事件的   
public void multicastEvent(ApplicationEvent event) { this.multicastEvent(event, this.resolveDefaultEventType(event));    }     public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event); Executor executor = this.getTaskExecutor(); Iterator var5 = this.getApplicationListeners(event, type).iterator(); while(var5.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var5.next(); if (executor != null) { executor.execute(() -> { this.invokeListener(listener, event); }); } else { this.invokeListener(listener, event); } } }
  //省略N個方法。。。
}

 

監聽器

下面來看一下springboot中都有哪些監聽器

這些個監聽器先不要管,一會我會分析幾個具體監聽器。

 

事件

這些事件在SpringApplication初始化過程中基本都會用到,我會分析其中的一兩個事件

 

觸發場景

看一下SpringApplication.run()方法

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
     //這里就是springboot比較講究的地方,一會分析這個 SpringApplicationRunListeners listeners
= this.getRunListeners(args);
     //這里就是觸發場景 listeners.starting(); Collection exceptionReporters;     
//省略N行不相關的 }

 

進入this.getRunListeners(args);

 private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
     //這里新建對象,我們看一下這個對象,里面的參數this.getSpringFactoriesInstances這個就是從META-INF/spring-factories中獲取監聽器的,返回一個實例化好列表,但是這里注意
//這里后去的監聽器是SpringApplicationListener,和上面初始化監聽器列表獲取的是不一樣的
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }

進入new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));

class SpringApplicationRunListeners {
    private final Log log;
    private final List<SpringApplicationRunListener> listeners;
   
    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList(listeners);
    }
  //SpringApplication開始啟動事件 void starting() {
        Iterator var1 = this.listeners.iterator();

        while(var1.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
            listener.starting();
        }

    }
  //SpringApplication環境准備就緒事件 void environmentPrepared(ConfigurableEnvironment environment) {
        Iterator var2 = this.listeners.iterator();

        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.environmentPrepared(environment);
        }

    }
  //SpringApplication上下文准備事件 void contextPrepared(ConfigurableApplicationContext context) {
        Iterator var2 = this.listeners.iterator();

        while(var2.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
            listener.contextPrepared(context);
        }

    }
}

 這個類的作用是什么呢?他的類名后面也帶有Listener,但是他並不是一個監聽器,而是一個封裝了目標中會產生的各個事件的封裝類,在上一篇文章中,我們是直接在具體目標中實例化事件,這里他沒有在具體目標中直接實例化目標,而是讓事件的實例化和具體目標分開,這樣可以讓系統的耦合性降低。

這個類中有一個列表:List<SpringApplicationRunListener> listeners;我們看一下這個SpringApplicationRunListener

public interface SpringApplicationRunListener {
    default void starting() {
    }

    default void environmentPrepared(ConfigurableEnvironment environment) {
    }

    default void contextPrepared(ConfigurableApplicationContext context) {
    }

    default void contextLoaded(ConfigurableApplicationContext context) {
    }

    default void started(ConfigurableApplicationContext context) {
    }

    default void running(ConfigurableApplicationContext context) {
    }

    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }
}

這個接口就是定義了各種要執行的事件,然后我們看一下這個接口的實現類,就只有一個實現類

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    private final SpringApplication application;
    private final String[] args;
   //這個里面有一個廣播器屬性,其實就是我們說的具體目標
private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); Iterator var3 = application.getListeners().iterator(); while(var3.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var3.next(); this.initialMulticaster.addApplicationListener(listener); } } public int getOrder() { return 0; } public void starting() { this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } public void contextPrepared(ConfigurableApplicationContext context) { this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context)); }
//省略N個方法。。。 }

 springboot就是使用這個玩意來連接觸發場景和調用具體的監聽器去執行的

 

OK,我們接下來來分析SimpleApplicationEventMulticaster的multicastEvent()方法

public void multicastEvent(ApplicationEvent event) {
        this.multicastEvent(event, this.resolveDefaultEventType(event));
    }
  
    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Executor executor = this.getTaskExecutor();
     //<1.1>獲取對這個事件感興趣的監聽器 Iterator var5
= this.getApplicationListeners(event, type).iterator(); while(var5.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var5.next(); if (executor != null) { executor.execute(() -> { this.invokeListener(listener, event); }); } else {
          //<1.2>具體讓監聽器執行事件的方法
this.invokeListener(listener, event); } } }

 

我們進入<1.1>,this.getApplicationListeners(event, type).iterator();

protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
        Object source = event.getSource();
        Class<?> sourceType = source != null ? source.getClass() : null;
        AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
     //先從緩存中獲取,由於我們初次啟動,緩存中是不存在的 AbstractApplicationEventMulticaster.ListenerRetriever retriever
= (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {  //添加同步鎖 synchronized(this.retrievalMutex) {
//再次從緩存中獲取,防止在執行這個操作的過程中,別的線程向里面寫了數據 retriever
= (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey); if (retriever != null) { return retriever.getApplicationListeners(); } else { retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
            //這一步就是獲取對這個事件感興趣的監聽器的關鍵步驟 Collection
<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever); this.retrieverCache.put(cacheKey, retriever); return listeners; } } } else { return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null); } }

 

我們進入this.retrieveApplicationListeners(eventType, sourceType, retriever);

private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
     //新建list,用來存放對該事件感興趣的監聽器 List
<ApplicationListener<?>> allListeners = new ArrayList(); LinkedHashSet listeners; LinkedHashSet listenerBeans; synchronized(this.retrievalMutex) {
       //這里this.defaultRetriever.applicationListeners就是在SpringApplication初始化的時候從META-INF/spring-factories中獲取的監聽器 listeners
= new LinkedHashSet(this.defaultRetriever.applicationListeners); listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans); }        Iterator var7 = listeners.iterator();      //把系統中所有的監聽器都遍歷一遍,找到對這個事件感興趣的監聽器 while(var7.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var7.next();
       //這里就是判斷是否感興趣的核心方法,我們進入看看
if (this.supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { retriever.applicationListeners.add(listener); } allListeners.add(listener); } }      //第一次初始化,listenerBeans是空的,這里就不分析了 if (!listenerBeans.isEmpty()) { ConfigurableBeanFactory beanFactory = this.getBeanFactory(); Iterator var15 = listenerBeans.iterator(); while(var15.hasNext()) { String listenerBeanName = (String)var15.next(); try { if (this.supportsEvent(beanFactory, listenerBeanName, eventType)) { ApplicationListener<?> listener = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class); if (!allListeners.contains(listener) && this.supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { if (beanFactory.isSingleton(listenerBeanName)) { retriever.applicationListeners.add(listener); } else { retriever.applicationListenerBeans.add(listenerBeanName); } } allListeners.add(listener); } } else { Object listener = beanFactory.getSingleton(listenerBeanName); if (retriever != null) { retriever.applicationListeners.remove(listener); } allListeners.remove(listener); } } catch (NoSuchBeanDefinitionException var11) { } } }

 

進入this.supportsEvent(listener, eventType, sourceType)

protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
     //先判斷監聽器是不是GenericApplicationListener GenericApplicationListener smartListener
= listener instanceof GenericApplicationListener ? (GenericApplicationListener)listener : new GenericApplicationListenerAdapter(listener);
     //這里是判斷是不是感興趣的
return ((GenericApplicationListener)smartListener).supportsEventType(eventType) && ((GenericApplicationListener)smartListener).supportsSourceType(sourceType); }

 

進入((GenericApplicationListener)smartListener).supportsEventType(eventType) 

public boolean supportsEventType(ResolvableType eventType) {
     //先判斷這個監聽器是不是SmartApplication類型的
if (this.delegate instanceof SmartApplicationListener) { Class<? extends ApplicationEvent> eventClass = eventType.resolve();
        //<3.1> 如果是SmartApplicationListener類型的,可以直接使用delegate中的supportsEventType方法判斷,為什么可以這樣判斷呢?
        //因為在SmartApplicationListener接口中有這個抽象方法,他的實現類都會實現這個方法
return eventClass != null && ((SmartApplicationListener)this.delegate).supportsEventType(eventClass); } else {
    //這個declaredEventType在
GenericApplicationListenerAdapter初始化的時候已經賦值了,大家可以看一下這個類的構造方法
    //<3.2> declaredEventType.isAssignableFrom(eventType); 這個方法就是判斷當前監聽器是否對這個事件感興趣的,這個怎么判斷呢?后面會說


 return this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType); } }

 

ok,<3.1>中,我們就找一個實現了SmartApplicationListener接口的監聽器,在我上面的截圖中的ConfigFileApplicationListener,這個監聽器實現了

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
  //可以發現這個監聽器對兩個事件感興趣,只要是下面的兩個事件,他都會返回true
  public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType) || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
    }
}

 

<3.2>處, this.declaredEventType.isAssignableFrom(eventType);這個方法有什么有呢?

我舉個例子大家就明白了。

在上面截圖的監聽器中有一個CloudFoundryVcapEnvironmentPostProcessor,我們看一下這個類的定義

public class CloudFoundryVcapEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered, ApplicationListener<ApplicationPreparedEvent> {}

 這個類在實現ApplicationListener的時候指定了具體的事件,就是ApplicationPreparedEvent,那<3.2>處的代碼,假設當前正在遍歷的就是這個監聽器,那這個方法就是判斷當前的事件是不是ApplicationPreparedEvent,如果是就返回true

 

也許分析到這里大家已經找不着北了,spring的代碼寫的就是這種,一層套一層,,,,,,,,,,,不過我們的分析還沒有完成,我們在上面已經找到了對ApplicationStartingEvent事件感興趣的監聽器,如下:

我們找到了4個對這個事件感興趣的監聽器,那我們要執行對應的回調方法,我們拿其中的一個監聽器舉例,就拿第一個LoggingApplicationListener,我們看一下這個監聽器會做什么吧,😄

public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent) {
            this.onApplicationStartingEvent((ApplicationStartingEvent)event);
        } else if (event instanceof ApplicationEnvironmentPreparedEvent) {
            this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
        } else if (event instanceof ApplicationPreparedEvent) {
            this.onApplicationPreparedEvent((ApplicationPreparedEvent)event);
        } else if (event instanceof ContextClosedEvent && ((ContextClosedEvent)event).getApplicationContext().getParent() == null) {
            this.onContextClosedEvent();
        } else if (event instanceof ApplicationFailedEvent) {
            this.onApplicationFailedEvent();
        }

    }

可以發現這個監聽器可以處理很多的事件,基本上就是打印日志。

 

 


以上,基本上把監聽器的原理給介紹完了,總結一下,從觸發場景產生事件開始,到尋找對這個事件感興趣的監聽器,在最后執行回調,就是整個監聽器的執行鏈路,在判斷監聽器是否對事件感興趣的時候,把監聽器分為兩種,一種是實現了SmartApplicationListener的,另一個種是實現了ApplicationListener的,前者會調用監聽器內部的supportEventType()方法來判斷當前監聽器是否對該事件感興趣,后者則是直接調用declaredEventType.isAssignableFrom(eventType);來判斷當前監聽器是否對該事件感興趣。下一篇文章我會自定義幾個監聽器,加入到springboot的啟動監聽器列表中,看一下執行的結果。

 

番外話:如果大家覺得這文章寫的又臭又長,很凌亂,那抱歉了,因為這個系列的文章就是我學習springboot過程中整理的一種類似筆記的東西,由於本身水平確實有限,各位看官老爺就多擔待。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM