public ConfigurableApplicationContext run(String... args) {
//1 创建StopWatch对象
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//2 设置Headless模式
configureHeadlessProperty();
//3 创建事件发布器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 将main方法的args参数封装到一个对象中
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//4 准备容器环境信息
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//5.打印Banner
Banner printedBanner = printBanner(environment);
//6.创建IOC容器和Bean工厂
context = createApplicationContext();
//7.初始化异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//8.初始化IOC容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//9.刷新容器----》Spring源码解析
refreshContext(context);
//10.刷新后的处理
afterRefresh(context, applicationArguments);
//11.关闭计时器
stopWatch.stop();
if (this.logStartupInfo) {
//12.打印启动信息
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//13.发布容器启动事件
listeners.started(context);
//14.回调运行器
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//15.发布容器启动完成事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
十、afterRefresh:刷新后的处理
这里是留给子类的扩展,在容器刷新后可以实现拓展逻辑
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
十一、stopWatch.stop():关闭计时器
这里会记录SpringBoot启动花费了多长时间
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
//记录启动花费了多长时间
long lastTime = System.nanoTime() - this.startTimeNanos;
//总花费时间
this.totalTimeNanos += lastTime;
//将启动花费时间记录放入任务中
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
//将任务添加进集合
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
十二、打印启动信息
StartupInfoLogger会打印出SpringBoot启动时间(在计时器中保存)和JVM启动时间
private CharSequence getStartedMessage(StopWatch stopWatch) {
StringBuilder message = new StringBuilder();
message.append("Started ");
appendApplicationName(message);
message.append(" in ");
message.append(stopWatch.getTotalTimeMillis() / 1000.0);
message.append(" seconds");
try {
double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0;
message.append(" (JVM running for ").append(uptime).append(")");
}
catch (Throwable ex) {
// No JVM time available
}
return message;
}
十三、发布容器启动事件
void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
这里会进入EventPublishingRunListener中发布容器启动事件ApplicationStartedEvent
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
十四、回调运行器
获取容器中ApplicationRunner和CommandLineRunner实现类回调callRunner方法
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);
}
}
}
十五、发布容器启动完成事件
这里会进入EventPublishingRunListener中发布容器启动完成事件ApplicationReadyEvent
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
总结
SpringBoot在容器启动完成后就没有做什么处理,都是预留拓展点。
- 子类实现预留方法:afterRefresh
- 打印启动信息(容器启动时间、JVM运行时间)
- 发布ApplicationStartedEvent和ApplicationReadyEvent事件
- 回调运行器ApplicationRunner和CommandLineRunner