天天看點

Spring Boot啟動流程源碼分析Spring Boot啟動流程源碼分析

Spring Boot啟動流程源碼分析

版本:2.1.1.RELEASE

使用main方法啟動Spring Boot應用:

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

進入SpringApplication類的run方法最終實作位置:

public ConfigurableApplicationContext run(String... args) {
	    // 1. 啟動計時
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		// 2. 回調接口SpringBootExceptionReporter用于支援自定義spring應用程式啟動錯誤的報告
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 3. 配置啟用Java headless模式 
		configureHeadlessProperty();
		// 4. 擷取Spring應用run方法的監聽器集合并啟動所有的監聽器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
		    // 5. 提供對用于運作SpringApplication的參數的通路
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 6. 建立和配置環境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
		    // 7. 配置忽略BeanInfo類的加載
			configureIgnoreBeanInfo(environment);
			// 8. 列印Banner
			Banner printedBanner = printBanner(environment);
			// 9. 建立ApplicationContext
			context = createApplicationContext();
			// 10. 擷取異常報告執行個體清單
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 11. 準備應用上下文
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 12. 重新整理底層的ApplicationContext
			refreshContext(context);
			// 13. protected方法,應用上下文重新整理後,子類可實作此方法用于後續的操作
			afterRefresh(context, applicationArguments);
			// 14. 列印應用啟動資訊
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			// 15. 啟動實作了CommandLineRunner 和 ApplicationRunner 接口的類的run方法 
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
		    // 16. 在run 方法結束之前立即調用,釋出事件,應用程式已準備好接受服務請求
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
           

run方法源碼上注釋了大概流程,接下來繼續深入重點流程的源碼。

流程4,擷取Spring應用run方法的監聽器集合并啟動所有的監聽器:

getRunListeners(String[] args) 方法源碼:

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}
           

傳回SpringApplicationRunListeners 執行個體,直接看構造方法的第二個參數的getSpringFactoriesInstances方法實作:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		// 1. 擷取類加載器
		ClassLoader classLoader = getClassLoader();
		// 2. 擷取指定類型的工廠實作類的完全限定類名集合
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 3. 根據傳入的完全限定類名集合建立對應工廠執行個體
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
	    // 4. 根據工廠執行個體上的@Order注解指定的順序排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
           

該方法第一個參數這裡傳入的是SpringApplicationRunListener 接口,會擷取該接口所在類加載器下的“META-INF/spring.factories”屬性檔案

設定的接口實作org.springframework.boot.context.event.EventPublishingRunListener,然後調用該類的構造方法執行個體化:

public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
           

該類的主要作用是作為應用啟動過程的事件釋出監聽器,可以看到構造方法中執行個體化了一個簡單的應用事件多點傳播器SimpleApplicationEventMulticaster 并周遊添加應用啟動事件監聽器。

流程4最後調用listeners.starting() 啟動監聽器,實作源碼:

@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}
           

該方法多點傳播一個ApplicationStartingEvent執行個體(應用啟動事件),事件源是SpringApplication本身。

接下來就是解析事件類型并調用對應的事件監聽器了,感興趣的可以自己深入。深入之前需要對Spring事件機制有所了解,

推薦此文Spring事件機制。

流程6. 建立和配置環境:

prepareEnvironment(listeners, applicationArguments)方法實作:

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 建立環境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置環境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 釋出環境準備事件
		listeners.environmentPrepared(environment);
		// 綁定環境到此應用
		bindToSpringApplication(environment);
		// 判斷是否需要轉換環境
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		// 附加ConfigurationPropertySource支援到指定環境
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
           

其中,getOrCreateEnvironment() 方法實作:

private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		// 根據ClassPath存在的類推斷應用運作環境,以下都是web環境
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}
           

流程9. 建立ApplicationContext:

createApplicationContext方法實作:

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
				    // 1. 初始化并傳回org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext Class對象
					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);
				}
			}
			// 忽略異常捕獲代碼
		}
		// 2. 調用AnnotationConfigServletWebServerApplicationContext對象的預設構造方法執行個體化
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
           

流程11. 準備應用上下文:

prepareContext方法實作:

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		// 1. 設定環境
		context.setEnvironment(environment);
		// 2. 在ApplicationContext中應用任何相關的後置處理,這裡為context對象的BeanFactory執行個體DefaultListableBeanFactory添加轉換服務
		postProcessApplicationContext(context);
		// 3. 在context重新整理之前應用實作了ApplicationContextInitializer回調接口的執行個體進行context上下文對象的初始化
		applyInitializers(context);
		// 4. 釋出context初始化事件
		listeners.contextPrepared(context);
		// 5. 列印應用版本資訊和激活的配置檔案資訊active profile
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// 6. 添加名稱為springApplicationArguments,springBootBanner的單例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);
		}
		// 7. 加載源(即main方法所在的類對象)不可變的集合對象并注冊其bean到應用上下文
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		// 8. 釋出應用準備事件
		listeners.contextLoaded(context);
	}
           

應用上下文對象準備好了,接下來就進行重新整理上下文操作。

12. 重新整理底層的ApplicationContext:

refreshContext方法實作:

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
			    // 向JVM運作時注冊一個關機鈎子,在JVM關閉時同時關閉這個上下文。
				context.registerShutdownHook();
			}
			......
		}
	}
           

進入到ServletWebServerApplicationContext類的refresh(context)方法實作:

@Override
	public final void refresh() throws BeansException, IllegalStateException {
		try {
			super.refresh();
		}
		......
	}
           

發現是直接調用的父類AbstractApplicationContext的refresh方法實作:

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 1. 準備好重新整理上下文
			prepareRefresh();

			// 2. 告訴子類重新整理内部bean工廠
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 3. 準備bean工廠以用于此上下文中
			prepareBeanFactory(beanFactory);

			try {
				// 4. 允許在特定的ApplicationContext實作中注冊特殊的bean後置處理器
				postProcessBeanFactory(beanFactory);

				// 5. 執行個體化并調用所有已注冊的BeanFactoryPostProcessor bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// 6. 執行個體化并調用所有已注冊的BeanPostProcessor bean
				registerBeanPostProcessors(beanFactory);

				// 7. 初始化MessageSource用于目前上下文,提供參數化和i18n的支援
				initMessageSource();

				// 8. 初始化事件多路廣播用于目前上下文,預設使用SimpleApplicationEventMulticaster單例bean
				initApplicationEventMulticaster();

				// 9. 在特定上下文子類中初始化其他特殊bean。
				onRefresh();

				// 10. 檢查監聽器bean并注冊它們
				registerListeners();

				// 11. 執行個體化剩餘所有非懶加載的單例bean
				finishBeanFactoryInitialization(beanFactory);

				// 12. 最後一步: 釋出相應的事件
				finishRefresh();
			}
			catch (BeansException ex) {
				......
	            // 銷毀所有建立的單例來避免懸空資源
				destroyBeans();
				// 重置 'active' 辨別.
				cancelRefresh(ex);
				// 抛出異常給調用者
				throw ex;
			}
			finally {
				// 重置Spring的公共反射中繼資料緩存
				resetCommonCaches();
			}
		}
	}
           

第一點主要做了以下操作:

  • 清除本地中繼資料緩存(如果有的話),删除所有緩存的類中繼資料。
  • 設定其啟動日期和活動标志以及執行任何屬性源的初始化。

第二點主要做了以下操作:

  • 将成員變量 refreshed 設為 true。
  • 為 DefaultListableBeanFactory 指定一個用于序列化的id。

第三點主要做了以下操作:

  • 配置工廠的标準上下文特征,例如上下文的類加載器和後置處理程式。
  • 所有bean定義都已加載,但還沒有執行個體化bean。

第四點主要做了以下操作:

  • 注冊特定應用上下文的後置處理器bean
  • 掃描basePackage指定的包路徑
  • 注冊被注解的類,例如@Configuration

第五點主要做了以下操作:

  • 執行個體化并調用所有已注冊的BeanFactoryPostProcessor bean,如果給定顯式順序,則遵循顯式順序
  • 分别調用實作了BeanFactoryPostProcessor接口的bean。

第六點主要做了以下操作:

  • 執行個體化并調用所有已注冊的BeanPostProcessor bean,如果給定顯式順序,則遵循顯式順序
  • 分别調用實作了BeanPostProcessor接口的bean。

第九點主要做了以下操作:

  • 預設建立TomcatWebServer
  • 初始化WebApplicationContext和SerlvetContext參數

第十一點完成BeanFactory的初始化并執行個體化剩餘的單例bean:

finishBeanFactoryInitialization方法實作:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// 初始化用于此上下文的轉換服務
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// 如果沒有内嵌value解析器Bean則注冊一個(例如 PropertyPlaceholderConfigurer bean),主要用于解析${}占位符.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}
		
		// 盡早地初始化LoadTimeWeaverAware bean以允許盡早地注冊其變換器
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// 停止正用于類型比對的臨時類加載器
		beanFactory.setTempClassLoader(null);

		// 緩存所有bean定義的中繼資料,不接受後面的改變
		beanFactory.freezeConfiguration();

		// 執行個體化所有剩餘的(非懶加載)單例bean
		beanFactory.preInstantiateSingletons();
	}
           

其中重點看最後一步preInstantiateSingletons方法的實作:

@Override
	public void preInstantiateSingletons() throws BeansException {
        ......
        
		// 疊代一個beanDefinitionNames的副本以允許init方法,這些方法又輪流注冊新的bean定義。
		// 雖然這可能不是正常工廠引導程式的一部分,但它确實可以正常工作。
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// 觸發所有非懶加載單例bean的初始化...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			// 如果不是抽象的bean并且是非懶加載的單例bean,則進行
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			    // 判斷是否是FactoryBean,如果是則進一步判斷是否需要盡早的初始化bean,否則直接初始化bean
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					getBean(beanName);
				}
			}
		}

		// 觸發所有可用單例bean的afterSingletonsInstantiated方法回調...
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}
           

接下來看getBean(beanName)方法的底層實作,是直接調用doGetBean方法,傳回指定bean的執行個體,該執行個體可以是共享的或獨立的:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        // 1. 傳回bean名稱,必要時删除工廠字首,并将别名解析為規範名稱。
		final String beanName = transformedBeanName(name);
		Object bean;

		// 2. 急切地檢查單例緩存以手動地注冊單例
		Object sharedInstance = getSingleton(beanName);
		// 共享的單例bean執行個體不為空并且args為空
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
			    // 3. 判斷該目前bean是否在建立中
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			//  4. 擷取給定bean執行個體的對象,如果是FactoryBean,則為bean執行個體本身或其建立的對象。
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
		    // 其它情況暫不做深入研究,感興趣的讀者可以自行閱讀AbstractBeanFactory#doGetBean方法源碼
		    ......
        }
		return (T) bean;
	}
           

檢視第二點getSingleton方法的實作:

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		// 如果從singletonObjects單例bean緩存中擷取key為beanName的單例bean為空并且該單例bean
		// 目前在建立中(在整個工廠内)則從早期已執行個體化的單例bean緩存earlySingletonObjects中
		// 檢查beanName的單例對象,如果為空則進一步從singletonFactories單例工廠緩存中擷取beanName為key
		// 的BeanFactory,如果BeanFactory不為空則擷取到其管理的單例bean執行個體并将其緩存
		// 到earlySingletonObjects對象上,最後從singletonFactories緩存中移除管理該beanName
		// 執行個體的BeanFactory對象(解決循環引用)
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
           

幾個重要對象說明:

  • singletonObjects:單例bean對象的緩存,ConcurrentHashMap->{beanName:beanInstance}
  • earlySingletonObjects: 早期的單例bean對象的緩存,HashMap->{beanName:beanInstance}
  • singletonFactories:單例BeanFactory的緩存,HashMap->{beanName:beanFactory}, beanFactory->beanInstance

回到refresh方法的第十二點,最後完成上下文的重新整理操作,調用LifecycleProcessor的onRefresh方法并且釋出最終的ContextRefreshedEvent事件:

protected void finishRefresh() {
		// 清除此資源加載器中的所有資源緩存。
		clearResourceCaches();

		// 初始化此上下文的生命周期處理器DefaultLifecycleProcessor。
		initLifecycleProcessor();

		// 調用DefaultLifecycleProcessor的onRefresh方法
		getLifecycleProcessor().onRefresh();

		// 釋出最終的ContextRefreshedEvent事件
		publishEvent(new ContextRefreshedEvent(this));

		// 如果激活則參與到LiveBeansView MBean中
		LiveBeansView.registerApplicationContext(this);
	}
           

子類finishRefresh方法最後啟動相應的WebServer并釋出事件。

@Override
	protected void finishRefresh() {
		super.finishRefresh();
		WebServer webServer = startWebServer();
		if (webServer != null) {
			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
		}
	}
           

以上就是Spring Boot啟動流程源碼分析的完整内容,如果有問題歡迎提出!

繼續閱讀