看 SpringBoot 的启动流程源码的入口很好找,就是启动类的 SpringApplication.run(DemoApplication.class, args),点进run方法如下:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
可以看到 SpringApplication.run 就干了两件事,一是创建 SpringApplication 对象,二是启动 SpringApplication。
1.创建 SpringApplication 对象
SpringApplication 构造器如下:
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 保存主配置类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 保存web应用的类型
// 注:分为响应式web应用,servlet类型web应用和非web应用,在后面用于确定实例化applicationContext的类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置初始化器 [ApplicationContextInitializer类型],保存到 SpringApplication中
// getSpringFactoriesInstances 作用是读取spring.factories文件key=ApplicationContextInitializer对应的value并实例化
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器[ApplicationListener类型],保存到 SpringApplication中
// getSpringFactoriesInstances 作用是读取spring.factories文件key=ApplicationListener对应的value并实例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 保存主配置类
// 没啥特别作用,仅用于获取入口类class对象
this.mainApplicationClass = deduceMainApplicationClass();
}
1.1 getSpringFactoriesInstances()
getSpringFactoriesInstances 是设置初始化器和监听器的关键方法,用于去 META-INFO/spring.factories 中获取 ApplicationContextInitializer 和 ApplicationListener 类型。
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 = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 从类路径的META-INF处读取相应配置文件spring.factories,然后进行遍历,读取配置文件中Key(type)对应的value
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 将names的对象实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
根据入参type类型 ApplicationContextInitializer.class 从类路径的 META-INF 处读取相应配置文件spring.factories并实例化对应 Initializer。
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
和设置初始化器一个套路,通过getSpringFactoriesInstances函数实例化监听器。
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
1.2 deduceEnvironmentClass()
查找主配置类,查询的依据就是看哪个方法是否有main方法
private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
switch (this.webApplicationType) {
case SERVLET:
return StandardServletEnvironment.class;
case REACTIVE:
return StandardReactiveWebEnvironment.class;
default:
return StandardEnvironment.class;
}
}
上面分析完了 new SpringApplication(primarySources).run(args) 的第一步 new SpringApplication(),下面我们接着来看它是如何启动这个 SpringBootApplication的。
2.启动 SpringBootApplication
我们先来看看 run 方法,源码如下:
public ConfigurableApplicationContext run(String... args) {
// 计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建一个容器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 1).获取监听器集合对象
SpringApplicationRunListeners listeners = getRunListeners(args);
// 2).发布容器 starting 事件(通过spring的事件多播器)
listeners.starting();
try {
// 封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 3).根据扫描到的监听器对象和函数传入参数,进行环境准备。
// 1:获取或者创建环境 2:把命令行参数设置到环境中 3:通过监听器发布环境准备事件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印springboot的图标
Banner printedBanner = printBanner(environment);
// 4).创建容器
// 根据webApplicationType 来创建容器(通过反射创建)
context = createApplicationContext();
// 获取异常报告
// 和上面套路一样,读取spring.factories文件key SpringBootExceptionReporter对应的value
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 5).context前置处理,准备上下文环境
// 1:把环境设置到容器中 2: 循环调用 AppplicationInitnazlier 进行容器初始化工作
// 3:发布容器上下文准备完成事件 4:注册关于springboot特性的相关单例Bean 5:发布容器上下文加载完毕事件
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 6).刷新容器
// 和上面的一样,context准备完成之后,将调用 AbstractApplication#refresh 方法,去初始化IOC容器
// 同时还会启动内嵌 Tomcat
refreshContext(context);
// 7).后置操作
// 运行 ApplicationRunner 和 CommandLineRunner
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 8).发布容器启动事件
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 出现异常;调用异常分析保护类进行分析
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 发布容器运行事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
run 方法执行完毕,SpringBoot 项目就启动好了。
PS:关于上面核心8步的代码我后面再补,想了解的同学这里可以先参考这篇文章 …






还没有评论,来说两句吧...