文章仅从源码的角度探讨springboot2.x的原理,不探讨使用。
我们知道Springboot是靠着这段代码进行启动的。
public static void main(String[] args) {
SpringApplication.run(XxxApplication.class, args);
}
分析源码的话,理所当然从这里下手。
通过定位到源码,我们发现这里有两步。一步是初始化SpringApplication对象,一步是调用run方法进行来完成启动。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
首先看一下初始化SpringApplication对象:
对于SpringApplication初始化的源码,主要就是对相关的属性进行赋值。(this.xxx = xxx)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 【基本数值的初始化】
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
// 【配置资源加载器】
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 【配置primarySources】
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 【应用环境监测】
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 【设置引导注册初始化列表 -- 该方法标记为弃用,可能在某个版本后完全删除】
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
// 【配置系统初始化器】
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 【配置应用监听器】
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 【配置main方法所在的类】
this.mainApplicationClass = this.deduceMainApplicationClass();
}
下面看一下run方法,它的内容非常简短:
public ConfigurableApplicationContext run(String... args) {
// 计时器开始计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// Headless 模式赋值 : 工作在一个没有显示器键盘的环境
this.configureHeadlessProperty();
// 发送ApplicationStartingEvent -- 监听事件
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
// 配置环境模块
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 发送 ApplicationEnvironmentPreparedEvent
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = this.printBanner(environment);
// 创建应用上下文对象
context = this.createApplicationContext();
// 初始化失败分析器
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 关联SpringBoot 组件和应用上下文对象、在prepareContext 方法中发送ApplicationContextInitializedEvent
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 加载sources到context当中、发送ApplicationPreparedEvent、刷新上下文
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
// 计时器停止计时
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 发送ApplicationStartedEvent事件
listeners.started(context);
// 调用框架启动扩展类、发送ApplicationReadyEvent事件
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
表面上看只有三十多行代码,其实背后的代码量是非常高的。
上面我们大致清楚了springboot的启动,本质就是初始化SpringApplication对象后,调用run方法。在扣细节之前,有必要整体认识一下springboot启动做了哪些事情:
框架初始化分为:
- 配置资源加载器
- 配置primarySources(一般是我们的启动类)
- 应用环境的检测(springboot1.x版本有两种环境,标准环境和web环境,spingboot2.x添加了一种Reactive环境)
- 配置系统初始化器
- 配置应用监听器
- 配置main方法所在类
接着就是框架的基本启动
- 计时器开始计时
- Headless模式赋值
- 发送ApplicationStartingEvent
- 配置环境模块
- 发送ApplicationEnvironmentPreparedEvent
- 打印banner
- 创建应用上下文对象
- 初始化失败分析器
- 关联springboot组件与应用上下文对象
- 发送ApplicationContextInitalizedEvent
- 加载sources到context
- 发送ApplicationPreparedEvent
- 刷新上下文(完成bean的加载)
- 计时器停止计时
- 发送ApplicationStartedEvent事件
- 调用框架启动扩展类
- 发送ApplicationReadyEvent
通过以上步骤完成基本的启动,后面还有框架的自动化装配的内容:
- 收集配置文件中的配置工厂类
- 加载组件工厂
- 注册组件内定义 bean
文字描述,可能不太直观,这里用图示整体梳理一下:
下面一起扣细节吧。