1. 程式人生 > >SpringBoot 啟動流程原始碼筆記

SpringBoot 啟動流程原始碼筆記

springboot啟動一個專案程式碼例子如下

	SpringApplication.run(xxxx.class, args);

跟蹤進去如下

public static ConfigurableApplicationContext run(Class<?> primarySource,
		String... args) {
	return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[]
primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }

上面是建立了一個SpringApplication物件,然後呼叫其run方法

SpringApplication構造方法:

public SpringApplication(Class<?>... primarySources) {
	this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader,
Class<?>... primarySources) { //上面傳null this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //上面傳執行的主方法的類 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //三種類型:REACTIVE|NONE|SERVLET;REACTIVE對應spring5新增的webflux;SERVLET是傳統servlet
this.webApplicationType = deduceWebApplicationType(); //從META-INF/spring.factories檔案獲取ApplicationContextInitializer型別的類 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //從META-INF/spring.factories檔案獲取ApplicationListener型別的類 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //獲取執行的主方法的類 this.mainApplicationClass = deduceMainApplicationClass(); }

建立了SpringApplication物件後,接著執行它的run方法

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();//效能監控
	//Spring容器類
	ConfigurableApplicationContext context = null;
	//SpringBootExceptionReporter類
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();//設定系統的“java.awt.headless”屬性
	//從META-INF/spring.factories中獲得SpringApplicationRunListener型別的類,放進SpringApplicationRunListeners
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();//開始事件
	try {
		//命令列啟動的引數封裝到DefaultApplicationArguments
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		//【標記1】例項或配置好environment
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		//對系統中的“spring.beaninfo.ignore”屬性進行處理
		configureIgnoreBeanInfo(environment);
		//列印招牌
		Banner printedBanner = printBanner(environment);
		//【標記2】建立容器,三種類型
		context = createApplicationContext();
		//從META-INF/spring.factories中獲取SpringBootExceptionReporter型別的類
		exceptionReporters = getSpringFactoriesInstances(
				SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		//【標記3】準備容器
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);
		//重新整理容器
		refreshContext(context);
		//留給子類實現
		afterRefresh(context, applicationArguments);
		//效能監控停止
		stopWatch.stop();
		//列印資訊,主要關於啟動時間
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		//監聽器回撥started
		listeners.started(context);
		//執行ApplicationRunner、CommandLineRunner(還沒看懂怎麼啟動的)
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		//會回撥listener的failed方法
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		//監聽器回撥running方法
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

上面的springboot的啟動流程一目瞭然了,準備好環境environment,準備好容器context,然後載入好各種資源到容器中,接著啟動。

【標記1】例項或配置好environment

private ConfigurableEnvironment prepareEnvironment(
		SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// 建立Environment型別物件,三種:StandardServletEnvironment|StandardReactiveWebEnvironment|StandardEnvironment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	//配置Environment的一些屬性
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	//監聽器回撥environmentPrepared方法
	listeners.environmentPrepared(environment);
	//繫結environment(其實還沒看懂幹嘛)
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		//這裡再次檢測是否需要把environment轉為對應型別的environment,上面getOrCreateEnvironment裡不是判斷過了嗎??
		environment = new EnvironmentConverter(getClassLoader())
				.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	//這裡把environment中的propertySources成員中的configurationProperties鍵對應的值替換為SpringConfigurationPropertySources
	ConfigurationPropertySources.attach(environment);
	return environment;
}

【標記2】建立容器,三種類型

protected ConfigurableApplicationContext createApplicationContext() {
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			switch (this.webApplicationType) {
			case SERVLET:
				contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch...
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

【標記3】準備容器

private void prepareContext(ConfigurableApplicationContext context,
		ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments, Banner printedBanner) {
	//設定environment
	context.setEnvironment(environment);
	//設定bean名字生成器、resourceloader、classloader、ApplicationConversionService
	postProcessApplicationContext(context);
	//執行從META-INF/spring.factories中獲取的ApplicationContextInitializer型別的initialize方法
	applyInitializers(context);
	//監聽器回撥contextPrepared方法,容器已經準備好
	listeners.contextPrepared(context);
	//列印資訊:主要關於active profile的
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// Add boot specific singleton beans
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	//向beanFactory註冊了bean
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		//設定是否允許覆蓋bean
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	// Load the sources
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	//開始載入資源
	load(context, sources.toArray(new Object[0]));
	//監聽器回撥contextLoaded,容器已經載入完
	listeners.contextLoaded(context);
}

prepareContext方法中會執行ApplicationContextInitializer的initialize方法,執行完的時候,當prepareContext方法執行完容器已載入好了資源

總結一下SpringApplicationRunListener的回撥順序
SpringApplicationRunListener

SpringApplicationRunListener共7個方法,回撥順序為:

starting -> environmentPrepared -> contextPrepared -> contextLoaded -> started -> running

failed方法在running前執行有錯誤發現才回調

對應事件為:
ApplicationStartingEvent -> ApplicationEnvironmentPreparedEvent -> ApplicationContextInitializedEvent -> ApplicationPreparedEvent -> ApplicationStartedEvent -> ApplicationReadyEvent