想知道你的SpringBoot是如何啓動的嗎?來看!

SpringBoot啓動流程分析

SpringBoot啓動流程主要分兩部分,一部分是在創建SpringApplication的時候,一部分是在Run方法裏

在這裏插入圖片描述

1.首先來看SpringApplication構造函數
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 資源加載器
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        
 // 1. 可能的web應用程序類型的類型。
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        
// 2. 設置初始化應用context
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        
// 3.設置初始化監聽
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
      
// 4. 推演主程序類
		this.mainApplicationClass = deduceMainApplicationClass();
	}

在這裏插入圖片描述

1.可能的web應用程序類型的類型。

這段代碼是來推斷我們的應用是哪種web應用程序
在這裏插入圖片描述

2.設置初始化應用context

在設置初始化應用context的時候 ,是先執行了`getSpringFactoriesInstances(ApplicationContextInitializer.class)方法,參數是ApplicationContextInitializer.class字節碼對象
在這裏插入圖片描述
我們先來看看他是如何加載這些類的
先從緩存中拿,如果沒有從資源文件裏取
在這裏插入圖片描述
在這裏插入圖片描述
雙擊Shift搜索spring.factories可以看到它存在於以下工程中

在這裏插入圖片描述

從Map中根據org.springframework.context.ApplicationContextInitializer的類型拿到需要的類初始化類,斷點進入getOrDefault(factoryClassName, Collections.emptyList());方法,之後就是把加載到的類放到集合中備用

3.初始化監聽器類

和初始化應用context沒有什麼區別,
唯一不同的是getSpringFactoriesInstances(ApplicationListener.class))傳進去的是·ApplicationListener.class所以這裏就不再贅述。

4.推演主程序類

在這裏插入圖片描述

到這裏就完成了SpringBoot啓動過程中初始化SpringApplication的過程。

小結
SpringApplication的流程,大致可以分爲四個步驟:
推演web應用的類型(如果沒有加web依賴類型NONE)
初始化ApplicationContextInitializer
初始化ApplicationListener
推演出主程序類
通過這樣四個步驟就完成了第一步SpringApplication的初始化過程。

2.在來看Run方法都幹什麼了
public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
        //計時器開始
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置Headless模式,是在缺少顯示屏、鍵盤或者鼠標時的系統配置 
		// 默認爲true
		configureHeadlessProperty();
        //獲取所有的監聽器
		SpringApplicationRunListeners listeners = getRunListeners(args);
        //啓動監聽器
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //準備環境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            //配置忽略的bean
			configureIgnoreBeanInfo(environment);
            //打印banner
			Banner printedBanner = printBanner(environment);
            //創建容器
			context = createApplicationContext();
            //異常相關處理
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            //準本應用上下文
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //刷新容器
			refreshContext(context);
            //刷新容器後的擴展接口
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
            //發佈監聽應用上下文啓動完成
			listeners.started(context);
            //執行runner
			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;
	}

下面就對關鍵步驟進行解釋

1.獲取所有的監聽器

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
這塊代碼就很熟悉了,它的主要作用就是去META-INFO/spring.properties中加載配置SpringApplicationRunListener的監聽器如下:


# Run Listeners 
org.springframework.boot.SpringApplicationRunListener=\norg.springframework.boot.context.event.EventPublishingRunListener

顯然只有一個事件發佈監聽器類,拿到了EventPublishingRunListener啓動事件發佈監聽器,下一步就是開始啓動了listeners.starting();我們往下跟源碼看
在這裏插入圖片描述

啓動的時候實際上是又創建了一個ApplicationStartingEvent對象,其實就是監聽應用啓動事件。
其中initialMulticaster是一個SimpleApplicationEventMulticaster

@Override
	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);
			}
		}
	}
2.準備環境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
        //配置環境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
        
		ConfigurationPropertySources.attach(environment);
        //環境準備完成
		listeners.environmentPrepared(environment);
        
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
3.配置忽略的Bean
4.打印Banner

在這裏插入圖片描述
在這裏插入圖片描述

這個是可以自定義的,也可以是圖篇或是文本文件中的圖形。

5.創建容器

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
我們的環境是servlet,DEFAULT_SERVLET_WEB_CONTEXT_CLASS其實servlet通過反射的方式創建對象

6.異常錯誤處理

在這裏插入圖片描述

其實還是去META-INFO/spring.factories配置文件中加載SpringBootExceptionReporter類

7.準備應用上下文

這裏就會根據之前創建的上下文、準備的環境、以及監聽等準備應用上下文

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
         //設置環境參數
		context.setEnvironment(environment);
        //設置後處理應用上下文
		postProcessApplicationContext(context);
        //把從spring.properties中加載的org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,進行初始化操作
		applyInitializers(context);
        //發佈應用上下文事件
		listeners.contextPrepared(context);
        //打印啓動日誌
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
        //註冊一個名字是springApplicationArguments單例的bane
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
        //註冊一個名字是springBootBanner單例的bean
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
        //是否允許類重名
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
        //是否可以懶加載
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
        //獲取所有的資源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
        //創建BeanDefinitionLoader加載器加載所有的資源
		load(context, sources.toArray(new Object[0]));
        //發佈上下文事件
		listeners.contextLoaded(context);
	}
8.刷新應用上下文
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
            //準備刷新上下文
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
            //通知子類刷新內部工廠
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
            //準備Bean工廠
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
             // 允許在上下文子類中對bean工廠進行後處理。
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
                //調用上下文中註冊爲bean的工廠處理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
                //註冊後置處理器
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
                //初始化信息源
				initMessageSource();

				// Initialize event multicaster for this context.
                //初始化上下文事件發佈器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
                //初始化其他自定義bean
				onRefresh();

				// Check for listener beans and register them.
                //註冊監聽器
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
                //完成bean工廠初始化
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
                //完成刷新,清緩存,初始化生命週期,事件發佈等
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
                //銷燬Bean
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

在onRefresh()這個方法中去創建的Tomcat服務
在這裏插入圖片描述
在這裏插入圖片描述

創建了Tomcat對象,並設置參數
在這裏插入圖片描述
在這裏插入圖片描述

在finishRefresh方法中啓動tomcat服務
在這裏插入圖片描述
在這裏插入圖片描述

9.刷新後處理

afterRefresh()是個一空實現,留着後期擴展

10.發佈監聽應用啓動事件

調用context.publishEvent方法,發佈應用啓動事件ApplicationStartedEvent
在這裏插入圖片描述

11.執行Runner

獲取所有的ApplicationRunner和CommandLineRunner來初始化一些參數callRunner()是一個回調函數
在這裏插入圖片描述

12.發佈上下文準備完成的事件

這段代碼看上去似成相識,前面有很多類似的代碼,不同的是這裏上下文準備完成之後發佈了一個ApplicationReadyEvent事件,聲明一下應用上下文準備完成
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章