源码版本说明

本文源码采用版本为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,二维码:

SpringBoot源码分析之---SpringBoot项目启动类SpringApplication浅析的更多相关文章

  1. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  2. SpringBoot源码分析(二)启动原理

    Springboot的jar启动方式,是通过IOC容器启动 带动了Web容器的启动 而Springboot的war启动方式,是通过Web容器(如Tomcat)的启动 带动了IOC容器相关的启动 一.不 ...

  3. 从SpringBoot源码分析 配置文件的加载原理和优先级

    本文从SpringBoot源码分析 配置文件的加载原理和配置文件的优先级     跟入源码之前,先提一个问题:   SpringBoot 既可以加载指定目录下的配置文件获取配置项,也可以通过启动参数( ...

  4. Springboot源码分析之项目结构

    Springboot源码分析之项目结构 摘要: 无论是从IDEA还是其他的SDS开发工具亦或是https://start.spring.io/ 进行解压,我们都会得到同样的一个pom.xml文件 4. ...

  5. Spring源码分析专题 —— IOC容器启动过程(上篇)

    声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...

  6. neo4j源码分析1-编译打包启动

    date: 2018-03-22 title: "neo4j源码分析1-编译打包启动" author: "邓子明" tags: - 源码 - neo4j - 大 ...

  7. Envoy 源码分析--程序启动过程

    目录 Envoy 源码分析--程序启动过程 初始化 main 入口 MainCommon 初始化 服务 InstanceImpl 初始化 启动 main 启动入口 服务启动流程 LDS 服务启动流程 ...

  8. 【spring-boot 源码解析】spring-boot 依赖管理梳理图

    在文章 [spring-boot 源码解析]spring-boot 依赖管理 中,我梳理了 spring-boot-build.spring-boot-parent.spring-boot-depen ...

  9. k8s client-go源码分析 informer源码分析(2)-初始化与启动分析

    k8s client-go源码分析 informer源码分析(2)-初始化与启动分析 前面一篇文章对k8s informer做了概要分析,本篇文章将对informer的初始化与启动进行分析. info ...

随机推荐

  1. Kubernetes --- 详细介绍和架构详解

    Kubernetes是一个跨主机集群的开源的容器调度平台,它可以自动化应用容器的部署,扩展和操作,提供以容器为中心的基础架构 目录 一. Kubernetes用途 二. Kubernetes特点 三. ...

  2. LeetCode go

    用Go语言刷LeetCode记录,只是为了练习Go语言,能力有限不保证都是最优解,只能在此抛转引玉了. 数据结构和算法 数据结构和算法是程序员的命根子,没了命根子也就没有了尊严. 1. 两数之和 题目 ...

  3. Python学习之旅:用Python制作一个打字训练小工具

    一.写在前面 说道程序员,你会想到什么呢?有人认为程序员象征着高薪,有人认为程序员都是死肥宅,还有人想到的则是996和 ICU. 别人眼中的程序员:飞快的敲击键盘.酷炫的切换屏幕.各种看不懂的字符代码 ...

  4. 使用openlivewriter编写cnblogs博客

    下载OpenLiveWriter 下载地址:http://openlivewriter.org/ 安装OpenLiveWriter 1.账号配置 2.常规操作,省略- 安装高亮插件 1.下载插件:ht ...

  5. 在VMware中就显示lo回环IP:127.0.0.1的解决办法。

    在VMware时由于某些原因导致,在使用ifconfig只会显示lo,不显示其他的东西 步骤:1.sudo lshw -numeric -class network 2.sudo route -nv ...

  6. Spring入门(十二):Spring MVC使用讲解

    1. Spring MVC介绍 提到MVC,参与过Web应用程序开发的同学都很熟悉,它是展现层(也可以理解成直接展现给用户的那一层)开发的一种架构模式,M全称是Model,指的是数据模型,V全称是Vi ...

  7. Tomcat类加载器体系结构

    <深入理解java虚拟机>——Tomcat类加载器体系结构 标签: java / 虚拟机 / tomcat Tomcat 等主流Web服务器为了实现下面的基本功能,都实现了不止一个自定义的 ...

  8. Metasploit工具----漏洞利用模块

    漏洞利用是指由渗透测试者利用一个系统.应用或者服务中的安全漏洞进行的攻击行为.流行的渗透攻击技术包括缓冲区溢出.Web应用程序攻击,以及利用配置错误等,其中包含攻击者或测试人员针对系统中的漏洞而设计的 ...

  9. Java8之熟透Lambda表达式

    一.Lambda简述 1.1.Lambda概述 ​ Lambda 表达式可以理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表. ...

  10. netCDF4 not installed properly - DLL load failed (netCDF4安装问题)

    环境描述:windows10 ,conda,python3.6 问题描述:netCDF4是python中用来处理地球气象数据的文件读取包,在安装完成后,from netCDF4 import Data ...