SpringBoot源碼分析之---SpringBoot項目啟動類SpringApplication淺析


源碼版本說明

本文源碼采用版本為SpringBoot 2.1.0BUILD,對應的SpringFramework 5.1.0.RC1

注意:本文只是從整體上梳理流程,不做具體深入分析

SpringBoot入口類

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

  這是我們日常使用springboot開發見到次數最多的引導類了,完成這個類的編寫,就完成了一個springboot項目的框架,springboot就回自動為我們完成一些默認配置,並幫我們初始化上下文容器,但細節我們是不知道的,下面我們就一起探索下SpringApplication.run(DemoApplication .class, args);這行代碼背后的故事!

SpringApplication初始化階段

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

// 初始化准備階段
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	// 參數初始化
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 推斷應用類型
	this.webApplicationType = deduceWebApplicationType();
	// 加載ApplicationContextInitializer系列初始化器(從spring.factories文件加載,並實例化和排序后存到this.initializers)
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	// 加載ApplicationListener系列監聽器(從spring.factories文件加載,並實例化和排序后存到this.listeners)
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 推斷應用入口類(main函數所在類)
	this.mainApplicationClass = deduceMainApplicationClass();
}

推斷應用類型

// 推斷應用類型
this.webApplicationType = deduceWebApplicationType();
private WebApplicationType deduceWebApplicationType() {
	if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
			&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
			&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
		return WebApplicationType.REACTIVE;
	}
	for (String className : WEB_ENVIRONMENT_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return WebApplicationType.NONE;
		}
	}
	return WebApplicationType.SERVLET;
}
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
		+ "web.reactive.DispatcherHandler";

private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
		+ "web.servlet.DispatcherServlet";

private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
		"org.springframework.web.context.ConfigurableWebApplicationContext" };

根據當前應用ClassPath下是否存在相關類,來確定應用類型。

加載ApplicationContextInitializer系列初始化器

// 加載ApplicationContextInitializer系列初始化器(從spring.factories文件加載,並實例化和排序后存到this.initializers)
setInitializers((Collection) getSpringFactoriesInstances(
		ApplicationContextInitializer.class));
public void setInitializers(
		Collection<? extends ApplicationContextInitializer<?>> initializers) {
	this.initializers = new ArrayList<>();
	this.initializers.addAll(initializers);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
		Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
		Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
		Set<String> names) {
	List<T> instances = new ArrayList<>(names.size());
	for (String name : names) {
		try {
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			Assert.isAssignable(type, instanceClass);
			Constructor<?> constructor = instanceClass
					.getDeclaredConstructor(parameterTypes);
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException(
					"Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}

  利用Spring工廠加載機制,實例化ApplicationContextInitializer接口的實現類,被加載的實現類都配置在MATE-INF/spring.factories文件中,getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args)這個方法就負責加載配置類並實例化和排序后返回,后面監聽器、異常收集器和Runner等也是通過這個類實現實例化對應實現類的。下面是spring-boot-autoconfigure\src\main\resources\META-INF\spring.factories文件的配置內容。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

加載ApplicationListener系列監聽器

// 加載ApplicationListener系列監聽器(從spring.factories文件加載,並實例化和排序后存到this.listeners)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
	this.listeners = new ArrayList<>();
	this.listeners.addAll(listeners);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return getSpringFactoriesInstances(type, new Class<?>[] {});
}

  如上面的初始化器,ApplicationListener系列監聽器也是通過getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)這個方法完成加載、排序及實例化的,而后存入到this.listeners中。

推斷應用入口類

// 推斷應用入口類(main函數所在類)
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
	try {
		// 通過new一個運行時異常獲取堆棧信息
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			// 找到main函數所在的入口類
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

  推斷應用入口類這部分比較有意思,他是通過new了一個運行時異常來拿到main線程的堆棧信息,遍歷所有方法找到main方法所在的類。

運行階段

// 運行階段
public ConfigurableApplicationContext run(String... args) {
	// 初始化容器啟動計時器
	StopWatch stopWatch = new StopWatch();
	// 開始計時
	stopWatch.start();
	// 初始化上下文ConfigurableApplicationContext
	ConfigurableApplicationContext context = null;
	// 初始化異常收集器
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	// 配置系統參數"java.awt.headless"
	configureHeadlessProperty();
	// 獲取SpringApplicationRunListener系列監聽器(從spring.factories文件加載,並實例化和排序)
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 遍歷所有SpringApplicationRunListener系列監聽器,廣播ApplicationStartingEvent
	listeners.starting();
	try {
		// 處理args參數
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		// 准備環境(創建、配置、綁定環境、廣播ApplicationEnvironmentPreparedEvent)
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		configureIgnoreBeanInfo(environment);
		// 打印Banner
		Banner printedBanner = printBanner(environment);
		// 根據應用類型創建上下文
		context = createApplicationContext();
		// 獲取SpringBootExceptionReporter系列異常收集器(從spring.factories文件加載,並實例化和排序)
		exceptionReporters = getSpringFactoriesInstances(
				SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		// 上下文前置處理(執行ApplicationContextInitializer系列初始化器、加載資源、廣播ApplicationPreparedEvent)
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);
		// 刷新上下文()
		refreshContext(context);
		// 上下文后置處理(目前啥也沒干)
		afterRefresh(context, applicationArguments);
		// 啟動完成,打印用時
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		// 遍歷前面設置的ConfigurableApplicationContext監聽器,發布ApplicationStartedEvent
		listeners.started(context);
		// 按順序回調實現了ApplicationRunner或CommandLineRunner接口的Runners
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		// 處理異常(發布ExitCodeEvent和ApplicationFailedEvent事件、異常收集器處理異常)
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		// 遍歷前面設置好的SpringApplicationRunListener監聽器,發布ApplicationReadyEvent
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

獲取SpringApplicationRunListener系列監聽器

// 獲取SpringApplicationRunListener系列監聽器(從spring.factories文件加載,並實例化和排序)
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
			SpringApplicationRunListener.class, types, this, args));
}

  這里我們再一次見到getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args)這個方法,功能同上。

廣播ApplicationStartingEvent事件

// 遍歷所有SpringApplicationRunListener系列監聽器,廣播ApplicationStartingEvent
listeners.starting();
public void starting() {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.starting();
	}
}
public void starting() {
	this.initialMulticaster.multicastEvent(
			new ApplicationStartingEvent(this.application, this.args));
}

  這里會遍歷上面拿到的排序好的所有SpringApplicationRunListener系列監聽器,廣播ApplicationStartingEvent事件,這代表Spring應用開始啟動,在這之前只進行了注冊化初始化器和監聽器。

准備環境、廣播ApplicationEnvironmentPreparedEvent事件

// 准備環境(創建、配置、綁定環境、廣播ApplicationEnvironmentPreparedEvent)
ConfigurableEnvironment environment = prepareEnvironment(listeners,
		applicationArguments);
private ConfigurableEnvironment prepareEnvironment(
		SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// Create and configure the environment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	listeners.environmentPrepared(environment);
	bindToSpringApplication(environment);
	if (this.webApplicationType == WebApplicationType.NONE) {
		environment = new EnvironmentConverter(getClassLoader())
				.convertToStandardEnvironmentIfNecessary(environment);
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}
public void environmentPrepared(ConfigurableEnvironment environment) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.environmentPrepared(environment);
	}
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
	this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
			this.application, this.args, environment));
}

  這里開始創建、配置和綁定ConfigurableEnvironment環境,環境准備好之后開始遍歷SpringApplicationRunListener系列監聽器,廣播ApplicationEnvironmentPreparedEvent事件,代表環境已准備好。

開始打印Banner

// 打印Banner
Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
	// Mode.OFF:不打印banner
	if (this.bannerMode == Banner.Mode.OFF) {
		return null;
	}
	// 加載banner資源,如果自定義了banner樣式,在這里加載,否則加載默認banner
	ResourceLoader resourceLoader = (this.resourceLoader != null)
			? this.resourceLoader : new DefaultResourceLoader(getClassLoader());
	// 初始化bannerPrinter
	SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
			resourceLoader, this.banner);
	// Mode.LOG:通過日志打印banner
	if (this.bannerMode == Mode.LOG) {
		return bannerPrinter.print(environment, this.mainApplicationClass, logger);
	}
	// 默認通過控制台打印banner
	return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

  關於Banner,SpringBoot支持關閉banner打印、打印到log日志和打印到system日志三種方式;同時支持自定義banner,自定義banner又有圖片和txt文本兩種(同時存在時先打印圖片banner,在打印文本banner),圖片banner又支持gif, jpg, png這三種類型的圖片格式banner(git優先於jpg優先於png),自定義banner非常簡單,只需要將banner文件放到classpath:下就好了(resources目錄下),如果存在多個banner文件,想指定某一個文件,只需要在application.properties文件加入如下配置就好了,非常方便。

spring.banner.image.location=banner.png
spring.banner.location=banner.txt

根據應用類型創建上下文

// 根據應用類型創建上下文
context = createApplicationContext();
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 (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, "
							+ "please specify an ApplicationContextClass",
					ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
		+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
		+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
		
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
		+ "annotation.AnnotationConfigApplicationContext";

  開始創建ConfigurableApplicationContext上下文,其中Servlet類型的web應用會創建AnnotationConfigServletWebServerApplicationContext類型的上下文,Reactive類型的web應用會創建AnnotationConfigReactiveWebServerApplicationContext類型的上下文,非web應用會創建AnnotationConfigApplicationContext類型的上下文。

獲取SpringBootExceptionReporter系列異常收集器

// 獲取SpringBootExceptionReporter系列異常收集器(從spring.factories文件加載,並實例化和排序)
exceptionReporters = getSpringFactoriesInstances(
		SpringBootExceptionReporter.class,
		new Class[] { ConfigurableApplicationContext.class }, context);
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
		Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	// Use names and ensure unique to protect against duplicates
	Set<String> names = new LinkedHashSet<>(
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}

  可以看到,加載異常收集器與上面初始化器和監聽器如出一轍,不做過多闡述。

上下文前置處理、廣播ApplicationPreparedEvent事件

// 上下文前置處理(執行ApplicationContextInitializer系列初始化器、加載資源、廣播ApplicationPreparedEvent)
prepareContext(context, environment, listeners, applicationArguments,
			printedBanner);
private void prepareContext(ConfigurableApplicationContext context,
		ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments, Banner printedBanner) {
	// 關聯上下文和環境
	context.setEnvironment(environment);
	//
	postProcessApplicationContext(context);
	// 去重並排序前面獲取好的ApplicationContextInitializer初始化器,執行初始化
	applyInitializers(context);
	// 遍歷前面設置好的SpringApplicationRunListener,但並沒有發布(目前什么都沒做,貌似為了以后擴展)
	listeners.contextPrepared(context);
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}

	// Add boot specific singleton beans
	// 添加啟動特定的單例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);
	}
	// Load the sources
	// 加載sources資源
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");

	load(context, sources.toArray(new Object[0]));
	// 廣播ApplicationPreparedEvent
	listeners.contextLoaded(context);
}

  在啟動上下文之前,會調用前面初始化好的ApplicationContextInitializer接口實現類,當然也包含我們自定義的,所以我們可以自定義初始化器,在上下文啟動前做一些操作。之后會廣播ApplicationPreparedEvent事件,通知SpringApplicationRunListener監聽器ConfigurableApplicationContext上下文已准備好(框架中用listeners.contextLoaded(context);方法廣播了ApplicationPreparedEvent事件,而ApplicationLoadedEvent事件並沒有發布,感覺這里以后還會變動。。。)。

刷新上下文

// 刷新上下文()
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
	refresh(context);
	if (this.registerShutdownHook) {
		try {
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
}

發布ApplicationStartedEvent事件

// 遍歷前面設置的ConfigurableApplicationContext監聽器,發布ApplicationStartedEvent
listeners.started(context);
public void started(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.started(context);
	}
}
@Override
public void started(ConfigurableApplicationContext context) {
	context.publishEvent(
			new ApplicationStartedEvent(this.application, this.args, context));
}

  發布ApplicationStartedEvent事件,通知 SpringApplicationRunListener系列監聽器ConfigurableApplicationContext上下文已啟動完成,Spring Bean 已初始化完成。

回調實現了ApplicationRunnerCommandLineRunner接口的Runners

// 按順序回調實現了ApplicationRunner或CommandLineRunner接口的Runners
callRunners(context, applicationArguments);
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);
		}
	}
}

  這里是有順序的,ApplicationRunner的實現類優先於CommandLineRunner的實現類被回調

發布ApplicationReadyEvent事件

try {
	// 遍歷前面設置好的SpringApplicationRunListener監聽器,發布ApplicationReadyEvent
	listeners.running(context);
}
public void running(ConfigurableApplicationContext context) {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.running(context);
	}
}
@Override
public void running(ConfigurableApplicationContext context) {
	context.publishEvent(
			new ApplicationReadyEvent(this.application, this.args, context));
}

  發布ApplicationReadyEvent事件,通知SpringApplicationRunListener系列監聽器ConfigurableApplicationContext上下文已經在運行,整個容器已經准備好。

發布ExitCodeEventApplicationFailedEvent事件和異常收集器收集異常信息

catch (Throwable ex) {
	handleRunFailure(context, ex, exceptionReporters, listeners);
	throw new IllegalStateException(ex);
}
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將會發布ExitCodeEvent事件通知上下文停止或重啟,並發布ApplicationFailedEvent事件通知SpringApplicationRunListener系列監聽器,最后SpringBootExceptionReporter異常收集器收集打印異常。

總結

SpringBoot啟動過程大致脈絡

  • 准備階段

    • 參數初始化
    • 推斷應用類型
    • 加載ApplicationContextInitializer系列初始化器
    • 加載ApplicationListener系列監聽器
    • 推斷應用入口類(main函數所在類)
  • 運行階段

    • 初始化容器啟動計時器,開始計時
    • 初始化上下文ConfigurableApplicationContext、異常收集器
    • 配置系統參數"java.awt.headless"
    • 獲取SpringApplicationRunListener系列監聽器
    • 遍歷所有SpringApplicationRunListener系列監聽器,廣播ApplicationStartingEvent
    • 處理args參數
    • 准備環境(創建、配置、綁定環境、廣播ApplicationEnvironmentPreparedEvent)
    • 配置忽略Bean信息
    • 打印Banner
    • 根據應用類型創建上下文
    • 獲取SpringBootExceptionReporter系列異常收集器
    • 上下文前置處理(執行ApplicationContextInitializer系列初始化器、加載資源、廣播ApplicationPreparedEvent)
    • 刷新上下文
    • 啟動完成,打印用時
    • 遍歷前面設置的ConfigurableApplicationContext監聽器,發布ApplicationStartedEvent
    • 回調實現了ApplicationRunner或CommandLineRunner接口的Runners
    • 遍歷前面設置好的SpringApplicationRunListener監聽器,發布ApplicationReadyEvent

  至此,整個SpringBoot項目已經啟動完成,我們可以看到,整個過程中Spring的事件驅動機制起着舉足輕重的作用,有了這個機制我們可以知曉容器的啟動過程,並且可以監聽到某些事件,對容器中我們關心的實例做進一步處理,我們深入理解事件驅動機制很有必要,它將幫助我們更好的理解和使用這個Spring框架體系。如果想要文中中文版SpringBoot注釋源碼,可以在我的github下載,如果發現哪里寫的不對,煩請留言通知我。

更多信息可以關注我的個人博客:逸竹小站

也歡迎關注我的公眾號:yizhuxiaozhan,二維碼:公眾號二維碼


免責聲明!

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



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