本文源碼採用版本爲
SpringBoot 2.1.0BUILD
,對應的SpringFramework 5.1.0.RC1
java注意:本文只是從總體上梳理流程,不作具體深刻分析react
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}複製代碼
這是咱們平常使用springboot開發見到次數最多的引導類了,完成這個類的編寫,就完成了一個springboot項目的框架,springboot就回自動爲咱們完成一些默認配置,並幫咱們初始化上下文容器,但細節咱們是不知道的,下面咱們就一塊兒探索下SpringApplication.run(DemoApplication .class, args);
這行代碼背後的故事!git
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下是否存在相關類,來肯定應用類型。github
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
這個方法就負責加載配置類並實例化和排序後返回,後面監聽器、異常收集器和Runner等也是經過這個類實現實例化對應實現類的。下面是spring-boot-autoconfiguresrcmainresourcesMETA-INFspring.factories
文件的配置內容。web
# 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系列監聽器(從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
// 推斷應用入口類(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方法所在的類。spring
// 運行階段
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
這個方法,功能同上。json
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應用開始啓動,在這以前只進行了註冊化初始化器和監聽器。springboot
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
事件,表明環境已準備好。websocket
// 打印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 已初始化完成。
ApplicationRunner
或CommandLineRunner
接口的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
上下文已經在運行,整個容器已經準備好。
ExitCodeEvent
和ApplicationFailedEvent
事件和異常收集器收集異常信息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項目已經啓動完成,咱們能夠看到,整個過程當中Spring的事件驅動機制起着舉足輕重的做用,有了這個機制咱們能夠知曉容器的啓動過程,而且能夠監聽到某些事件,對容器中咱們關心的實例作進一步處理,咱們深刻理解事件驅動機制頗有必要,它將幫助咱們更好的理解和使用這個Spring框架體系。若是想要文中中文版SpringBoot註釋源碼,能夠在個人github下載,若是發現哪裏寫的不對,煩請留言通知我。