Spring Boot源码学习(二) SpringBoot启动流程源码分析一
目录
Spring Boot源码学习(二) SpringBoot启动流程源码分析
一、概述
二、源码分析
1.run()方法总览
2.获取并启动监听器
(1)获取监听器
(2)启动监听器
3.构造容器环境
参考
一、概述
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域成为领导者。本文分析版本为Spring Boot 2.2.X
SpringBoot所具备的特征有:
(1)可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;
(2)内嵌Tomcat或Jetty等Servlet容器;
(3)提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;
(4)尽可能自动配置Spring容器;
(5)提供准备好的特性,如指标、健康检查和外部化配置;
(6)绝对没有代码生成,不需要XML配置。
二、源码分析
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(IcostMsBidApplication.class, args);
}
}
1.run()方法总览
查看run()方法
- 获取并启动监听器
- 构造容器环境
- 创建容器
- 实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
- 准备容器
- 刷新容器
- 刷新容器后的扩展接口
public ConfigurableApplicationContext run(String... args) {
//时间监控 创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//设置系统属性
configureHeadlessProperty();
//获取spring.factories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
//第一步:获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//初始化默认应用参数类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//第二步:构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//设置需要忽略的bean
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//第三步:创建容器 创建应用上下文
context = createApplicationContext();
//第四步:准备异常报告器 实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//第五步:准备容器 应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//第六步:刷新容器
refreshContext(context);
//第七步:刷新容器后的扩展接口
afterRefresh(context, applicationArguments);
//停止计时监控类
stopWatch.stop();
//输出日志记录执行主类名、时间信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布应用上下文启动完成事件
listeners.started(context);
//执行所有 Runner 运行器
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;
}
2.获取并启动监听器
(1)获取监听器
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));
}
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//防止重名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//整个 springBoot 框架中获取factories的方式统一如下
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
//整个 springBoot 框架中获取factories的方式统一如下
@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文件到内存
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;
}
args本身默认为空,但是在获取监听器的方法中,getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)
将当前对象作为参数
(2)启动监听器
listeners.starting() .获取的监听器为EventPublishingRunListener
,从名字可以看出是启动事件发布监听器,主要用来发布启动事件
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
EventPublishingRunListener这个是springBoot框架中最早执行的监听器,在该监听器执行started()方法时,会继续发布事件,也就是事件传递。这种实现主要还是基于spring的事件机制。
@Override
public void starting() {
//关键代码,这里是创建application启动事件ApplicationStartingEvent
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
因为我们的事件类型为ApplicationEvent,所以会执行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot会在运行过程中的不同阶段,发送各种事件,来执行对应监听器的对应方法。大同小异,别的监听器执行流程这里不再赘述,后面会有单独的详解。这里选了一个 springBoot 的日志监听器来进行讲解,核心代码如下:
@Override
public void onApplicationEvent(ApplicationEvent event) {
//Springboot启动的时候
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
//Springboot的Environment环境准备完成的时候
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
//在Springboot容器的环境设置完成以后
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
//容器关闭的时候
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
//容器启动失败的时候
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
3.构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
// 获取对应的ConfigurableEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//发布环境已准备事件,这是第二次发布事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
继续跟踪getOrCreateEnvironment()
方法,environment
已经被设置了servlet
类型,所以这里创建的是环境对象是StandardServletEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
Environment
接口提供了4种实现方式
-
StandardEnvironment
普通程序 -
StandardServletEnvironment
Web程序 -
MockEnvironment
测试程序的环境 -
StandardReactiveWebEnvironment
响应式web环境
枚举类WebApplicationType
是新增的特性
public enum WebApplicationType {
/**
* The application should not run as a web application and should not start an
* embedded web server.
*/
//不需要再web容器的环境下运行,普通项目
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
*/
//基于servlet的web项目
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
*/
//这个是spring5版本开始的新特性
REACTIVE;
}
执行到这里,系统变量和环境变量已经被载入到配置文件的集合中,接下来就行解析项目中的配置文件.主要来看一下ConfigFileApplicationListener
,该监听器非常核心,主要用来处理项目配置。项目中的 properties 和yml文件都是其内部类所加载。
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
List<EnvironmentPostProcessor> loadPostProcessors() {
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}
首先还是会去读spring.factories
文件,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
获取的处理类有以下四种:
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
执行完监听器流程后,ConfigFileApplicationListener
会执行该类本身的逻辑。由其内部类Loader
加载项目制定路径下的配置文件:
// Note the order is from least to most specific (last one wins)
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";