盤點 SpringBoot : Application 主流程

首先分享以前的全部文章 , 歡迎點贊收藏轉發三連下次必定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 備份 : 👉 gitee.com/antblack/ca…java

一 . 前言

這一篇來講一說 SpringBoot Application 的主流程.react

SpringAppliation 的主流程入口很簡單 :git

@SpringBootApplication
public class BaseApplication {
    public static void main(String[] args) {
        SpringApplication.run(BaseApplication.class, args);
    }
}

1 > 使用 @SpringBootApplication 註解,標明是 Spring Boot 應用。經過它,能夠開啓自動配置的功能。
2 > main 方法 : 調用 SpringApplication#run(Class<?>... primarySources) 方法,啓動 Spring Boot 應用

複製代碼

咱們從這個入口一步步看看 , 這個流程裏面到底作了什麼 , 其中主要涉及這幾件事:github

  • 建立且啓動 listener
  • 經過 args 生成 environment
  • 經過 environment 建立 context
  • 刷新 context
  • 固然 , 還會打印 Banner

流程圖web

SpringBoot 流程.png

二 . 流程解析

2.1 SpringApplication 核心流程

核心流程主要在SpringApplication.class 中 ,咱們從這個流程看看:spring

SpringApplication 屬性編程

F- resourceLoader
    ?- 資源加載器
F- primarySources 
    ?- 主要的 Java Config 類的數組
F- webApplicationType 
    ?- 調用 WebApplicationType#deduceFromClasspath() 方法,經過 classpath ,判斷 Web 應用類型。
F- listeners 
    ?- ApplicationListener 數組。
F- mainApplicationClass 
    ?- 調用 #deduceMainApplicationClass() 方法,得到是調用了哪一個 #main(String[] args) 方法
    
    
// 提供了三種 ApplicationContext 加載類 , 這個後續會用上
String DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext";
String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";


// 默認Banner 地址 -> "banner.txt"
String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
    

複製代碼

SpringApplication run 方法主流程數組

M1_01- run()
    T-> 計時 ,建立 StopWatch  而且啓動 , 用於記錄啓動的時長
    -> configureHeadlessProperty() : 配置 headless 屬性
        ?- Headless模式是系統的一種配置模式。在該模式下,系統缺乏了顯示設備、鍵盤或鼠標
        ?- 該模式下能夠建立輕量級組件 , 收集字體等前置工做
    - getRunListeners : 獲取 SpringApplicationRunListeners ,而且開啓監聽 listeners.starting()
    - 1 建立  ApplicationArguments 對象
    - 2 prepareEnvironment 加載屬性配置(傳入 listener + arguments ) -> M20_01
        ?- 執行完成後,全部的 environment 的屬性都會加載進來 (application.properties等)
    - 3 打印banner(printBanner) 
    - 4 建立Spring 容器(createApplicationContext)
    -   準備異常對象(getSpringFactoriesInstances.SpringBootExceptionReporter )
    - 5 調用全部初始化類的 initialize 方法(prepareContext) , 初始化Spring 容器
    - 6 刷新容器(refreshContext) , 執行 Spring 容器的初始化的後置邏輯(afterRefresh)
    T-> 計時完成    
    - 7 通知 SpringApplicationRunListener  , 執行異常處理等收尾
複製代碼

SpringApplication 主流程僞代碼markdown

public ConfigurableApplicationContext run(String... args) throws Exception {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 調用 M1_21 獲取 ConfigurableEnvironment
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		// PS:M1_01_03
		context = createApplicationContext();
                // 從 factories 中獲取 Exception M1_11
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
                // M1_35 : 爲 Context 添加屬性
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // M1_50 : 刷新容器Bean
		refreshContext(context);
		afterRefresh(context, applicationArguments);
                // 計時結束
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
                
                // 執行 implements ApplicationRunne 對象
		callRunners(context, applicationArguments);
	}catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}
	
	try {
		listeners.running(context);
	}catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}
複製代碼

2.2 子模塊 : Enviroment 處理

M1_21- prepareEnvironment
    - getOrCreateEnvironment -> M21_01
    - 

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    // 內部經過 WebApplicationType 生成不一樣的 Environment (能夠set 本身的 Environment) -> M1_23
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 重寫此方法以徹底控制環境自定義,或者重寫上述方法之一以分別對屬性源或概要文件進行細粒度控制。 -> M1_24
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 對 configurationProperties 屬性進行處理 -> M2_01
    ConfigurationPropertySources.attach(environment);
    // listener 處理
    listeners.environmentPrepared(environment);
    // 將 environment 綁定到 SpringApplication
    bindToSpringApplication(environment); -> M1_25 if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

M1_23- getOrCreateEnvironment
    - 經過 webApplicationType , 建立三種不一樣的 Environment

// M1_23 僞代碼
private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
    }
}


M1_24- configureEnvironment
    - 獲取 ApplicationConversionService 的實現類
    - 調用 configurePropertySources(environment, args) , 在此應用程序環境中添加、刪除或從新排序任何PropertySources。
        - MutablePropertySources sources = environment.getPropertySources();
            ?- 獲取前面的 MutablePropertySources
	- sources.addLast : 添加 defaultProperties
	- 若是 args > 0 , 且能夠添加 commonLine , 則添加CommandLineProperties
            ?- 其中會判斷屬性 commandLineArgs 是否會存在 ,存在則經常使用替換方式
            
// M1_24 僞代碼
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    configurePropertySources(environment, args);
    configureProfiles(environment, args);
}



C2- ConfigurationPropertySources.
    M2_01- attach(environment);
	- sources = ((ConfigurableEnvironment) environment).getPropertySources() : 獲取 MutablePropertySources
	- attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME) : 獲取 PropertySource
        - 若是 attached 爲null 或者 不等於 sources , 則將 sources 替換原先的 attached
        
// Binder : 從一個或多個容器綁定對象的容器對象
M1_25- bindToSpringApplication
C3- Binder
    M3_01- bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,boolean allowRecursiveBinding, boolean create)
        - context.clearConfigurationProperty() : 清空原屬性
        - handler.onStart(name, target, context) : BindHandler 開始綁定
        - bindObject : 將 屬性綁定到對象
        - handleBindResult : 返回 綁定結果
   
   
private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,boolean allowRecursiveBinding, boolean create) {
    try {
        Bindable<T> replacementTarget = handler.onStart(name, target, context);
        if (replacementTarget == null) {
            return handleBindResult(name, target, handler, context, null, create);
        }
        target = replacementTarget;
        Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
        return handleBindResult(name, target, handler, context, bound, create);
    }catch (Exception ex) {
        return handleBindError(name, target, handler, context, ex);
    }
}
        

複製代碼

configureIgnoreBeanInfo 配置併發

// 其中僅設置了2個屬性 : 

// 屬性一 : spring.beaninfo.ignore , 用於
environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE)

// 設置二 : 設置到 System 中
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString())

//那麼該屬性是爲了幹什麼 ? -> spring.beaninfo.ignore
當值爲 true 時 , 意味着跳過對 BeanInfo 類的搜索 . 
若是經歷了對不存在的 BeanInfo 類的重複 ClassLoader 訪問,能夠考慮將這個標誌切換爲「 true」,以防在啓動或延遲加載時這種訪問開銷很大

可是現階段全部 BeanInfo 元數據類,默認值是"false"

複製代碼

2.3 Banner 的打印

純粹是好奇 , 過來看一眼

M1_28- printBanner : 準備 Banner 類


// 代碼詳情
private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    ResourceLoader resourceLoader = (this.resourceLoader != null) 
        ? this.resourceLoader
        : new DefaultResourceLoader(getClassLoader());
        
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    // 核心代碼 -> 調用類 SpringBootBanner
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

// 核心類 SpringBootBanner
C- SpringBootBanner
    M- printBanner
        - printStream.println(line) : 逐行打印那個 Spring
        - 打印版本號
            ?-  :: Spring Boot ::        (v2.3.1.RELEASE)

// ps : 點進去就知道了 , 逐行打印

複製代碼
  • 提供了一個開關
  • 構建一個 SpringApplicationBannerPrinter ,
  • 調用 print 生成了一個 Banner 對象

2.4 createApplicationContext 邏輯

該邏輯爲建立 ApplicationContext 的相關邏輯 , 這裏先簡單過一下 :

C- SpringApplication
    M1_30- createApplicationContext
    	- Class<?> contextClass = this.applicationContextClass;
	    IF- contextClass爲null
         	- 根據 webApplicationType 類型,得到 ApplicationContext 類型
            - AnnotationConfigServletWebServerApplicationContext 
            - AnnotationConfigApplicationContext
            - AnnotationConfigReactiveWebServerApplicationContext
         - 根據 contextClass 建立 ApplicationContext 對象   
         
         
// M1_30 僞代碼
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                 // 根據不一樣 webApplicationType 準備不一樣的 ContextClass
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
            }
        }
        // 反射構造器方法得到 context 實現類
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    
    
// ApplicationContext 核心流程值得深刻 , 後面開一個單章來詳細是說
    

複製代碼

PS:M1_01_03 生成一個 ConfigurableApplicationContext data-applicationContext.jpg

PS : ApplicationContext 體系結構

ApplicationContext.png

這裏補充一下 Reactive 的區別

SpringBoot-Stack.jpg

如圖所示 , Reactive 是 Spring 中一個重要的技術棧 , Reactive 能夠用於構建響應性、彈性、彈性和消息驅動的企業級反應系統.

WebFlux 並非 Spring MVC 替代,它主要應用仍是在異步非阻塞編程模型上 , 使用了 WebFlux 的應用,其總體響應時間更短,啓動的線程數更少,使用的內存資源更少。同時,延遲越大,WebFlux 的優點越明顯

具體能夠參考這篇文檔 @ blog.csdn.net/u010862794/…

2.5 中間操做

M1_11 獲取 SpringBootExceptionReporter 的處理類

getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);


// Spring.factories
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

複製代碼

M1_12 callRunners : 幹什麼

該方法主要是運行 ApplicationRunner

// callRunners 主流程
private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}


@Component
public class SourceTestLogic implements ApplicationRunner {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("------> run <-------");
    }
}

// 簡單點說 , ApplicationRunner 初始化啓動就是這裏作的



複製代碼

2.6 Context 的三次處理

Context 的二次處理分爲 三步 :

  • prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    • 設置 Context 的前置屬性
  • refreshContext(context);
    • 注入 Bean , 註冊監聽器 , 初始化併發布事件
  • afterRefresh(context, applicationArguments);
    • 現階段爲空實現

prepareContext 的主流程

// prepareContext(context, environment, listeners, applicationArguments, printedBanner);
M1_35- prepareContext : 準備 ApplicationContext 對象,主要是初始化它的一些屬性
        -> 1 設置 context 的 environment 屬性
        -> 2 調用 #postProcessApplicationContext(ConfigurableApplicationContext context) 方法,
            ?- 設置 context 的一些屬性 -> M1_36
        -> 3 調用 #applyInitializers(ConfigurableApplicationContext context) 方法,
            ?- 初始化 ApplicationContextInitializer -> applyInitializers
        -> 4 調用 SpringApplicationRunListeners#contextPrepared
            ?- 通知 SpringApplicationRunListener 的數組,Spring 容器準備完成
        -> 5 設置 beanFactory 的屬性
        -> 6 調用 #load(ApplicationContext context, Object[] sources) 方法,加載 BeanDefinition 們
            ?- 
            -> 建立 BeanDefinitionRegistry 對象
            -> 設置 loader 屬性
            -> 執行BeanDefine 加載

// M1_35 僞代碼
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 爲容器設置 environment
        context.setEnvironment(environment);
        // 設置容器 classloader 和 conversionService , 即容器中類的加載工具
        postProcessApplicationContext(context);
        // 在刷新上下文以前,將任何ApplicationContextInitializers應用於該上下文
        applyInitializers(context);
        // listeners 執行 , 在建立並準備好ApplicationContext以後調用,但在加載源以前調用。
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
             // 打印啓動日誌配置
            logStartupInfo(context.getParent() == null);
             // 打印 active profile 信息
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        // beanFactory 注入相關Bean
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            // 添加一個新的BeanFactoryPostProcessor
            // 該新的BeanFactoryPostProcessor將在刷新以前應用於此應用程序上下文的內部Bean工廠,而後再評估任何Bean定義。 
            // 在上下文配置期間調用。
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // -> M1_38
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
}    

M1_36- postProcessApplicationContext : 在ApplicationContext中應用任何相關的後處理
	- beanNameGenerator 存在則設置到 context.getBeanFactory().registerSingleton 中
	- resourceLoader 存在且 爲 GenericApplicationContext類型 , 則 setResourceLoader
	- resourceLoader 存在且 爲 resourceLoader 則 setClassLoader
	- addConversionService 爲 true 則設置到 BeanFactory 中
     
// M1_36 僞代碼
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
          context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
    if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    }
}
    

M1_37- applyInitializers
    FOR-  (ApplicationContextInitializer initializer : getInitializers()) : for 循環處理 Initializers
        - Assert判斷 是否爲 ApplicationContextInitializer 的實例
        - initializer.initialize(context) 初始化對象
    
            
M1_38- load
    - 這裏主要是建立 BeanDefinitionLoader protected void load(ApplicationContext context, Object[] sources) {
    // 建立 BeanDefinitionLoader , 直接 new 的 
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        // beanName 生成類
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    // -> M5_01
    loader.load();
} 

C5- BeanDefinitionLoader
    M5_01- loader
        ?- 這裏會循環調用 load source
 
// M5_01 僞代碼
for (Object source : this.sources) {
    count += load(source);
}   

複製代碼

refresh 流程

C- AbstractApplicationContext
    M1_50- refreshContext(context);
        - refresh(context);
        - 若是有 ShutdownHook (關閉鉤子) , 則註冊 registerShutdownHook


    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 準備此上下文以進行刷新。
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 告訴子類刷新內部bean工廠
            prepareBeanFactory(beanFactory);

            try {
                // 容許在上下文子類中對bean工廠進行後處理
                postProcessBeanFactory(beanFactory);

                // 調用在上下文中註冊爲bean的工廠處理器
                // IOC 的主要邏輯就在其中
                invokeBeanFactoryPostProcessors(beanFactory);

                // 註冊攔截Bean建立的Bean處理器
                registerBeanPostProcessors(beanFactory);

                // 初始化此上下文的消息源
                initMessageSource();

                // 初始化此上下文的事件多主控器.
                initApplicationEventMulticaster();

                // 初始化特定上下文子類中的其餘特殊bean.
                onRefresh();

                // 檢查偵聽器bean並註冊它們.
                registerListeners();

                // 實例化全部剩餘的(非lazy-init)單例.
                finishBeanFactoryInitialization(beanFactory);

                //最後一步:發佈相應的事件
                finishRefresh();
            }catch (BeansException ex) {
            
                // 銷燬已建立的單件以免資源懸空
                destroyBeans();

                // 重置「活動」標誌.
                cancelRefresh(ex);

                // 將異常傳播到調用方.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }
    
// 這一部分也是 AbstractApplicationContext 的主要流程 , 放在後續 ApplicationContext 單章說
    

複製代碼

afterRefresh 流程

PS : 這是一個空實現

M1_60- afterRefresh


複製代碼

2.7 Listener 處理

Application 啓動時 , Listener 總共涉及到四個操做 :

  • getRunListeners
  • listener.starting
  • listener.started
  • listener.running

以我這微薄的英語水平 , 這裏面怕是有個進行時和一個過去時 , 哈哈哈哈哈
能夠看到 , 第一步和第四步目的都比較明確 , 主要來看看第二 . 三步

getRunListeners

C- SpringApplication
    M- getRunListeners
        - getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)
		
// 能夠看到 , 從 Factories 中獲取的 , 先階段只有一個 EventPublishingRunListener
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

複製代碼

listener.starting

//這裏的 starting 是 EventPublishingRunListener 運行
// 具體事件的處理邏輯 , 咱們後續文檔繼續深刻

C- EventPublishingRunListener
    M- starting()
        - this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	
C- AbstractApplicationEventMulticaster
    ?- 將全部事件多播給全部註冊的偵聽器,並在調用線程中調用它們 , 簡單點說就是事件羣發
	
	
	

複製代碼

listeners.started(context)

// 這裏的事件就更多了

for (SpringApplicationRunListener listener : this.listeners) {
    listener.started(context);
}

這裏仍是 EventPublishingRunListener
    
@Override
public void started(ConfigurableApplicationContext context) {
    context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
    AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}    


複製代碼

listener.running

// 發佈 ReadyEvent
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
複製代碼

篇幅有限 , 就不詳細看看哪些執行了

2.8 其餘處理

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
    try {
        try {
            // 處理退出碼
            handleExitCode(context, exception);
            if (listeners != null) {
                listeners.failed(context, exception);
            }
        }finally {
            reportFailure(exceptionReporters, exception);
            if (context != null) {
                context.close();
            }
        }
    }catch (Exception ex) {
        logger.warn("Unable to close ApplicationContext", ex);
    }
    ReflectionUtils.rethrowRuntimeException(exception);
}
	
複製代碼

三 . 總結

這一篇對 SpringBoot 的大概流程簡單過了一遍 , 篇幅有限有幾個點暫時先沒歸入 , 後續補充

  • @SpringApplication 註解
  • SpringApplicationContext 的始末
  • Listener 的加載邏輯

更新記錄

  • v20210804 : 更新佈局 , 優化結構
相關文章
相關標籤/搜索