回答
Spring Boot 的 main()
是整个 Spring Boot 项目的启动入口。它通过 SpringApplication.run()
完成了整个项目的启动过程,最终让一个 Web 项目运行起来。
Spring Boot 的启动过程分为以下几个主要步骤:
- 初始化 SpringApplication:
SpringApplication
类在启动时会做一些配置工作,比如确定项目是否是 Web 应用。 - 准备环境:Spring Boot 会加载运行时环境,比如系统属性、环境变量等,最终形成一个
Environment
对象。 - 创建并刷新 Spring 容器:加载所有的 Bean 定义,并完成 Bean 的初始化工作。
- 启动嵌入式 Web 容器:如果是 Web 项目,它会启动内嵌的
Tomcat
、Jetty
等 Web 服务器。 - 运行应用程序:执行用户自定义的逻辑,比如
@SpringBootApplication
中的CommandLineRunner
或ApplicationRunner
。
通过这些步骤,Spring Boot 的 main
方法将一个 Web 项目从启动到运行完美地串联起来。
详解
我们直接看 SpringApplication.run()
:
public ConfigurableApplicationContext run(String... args) {
// 1. 应用启动计时开始
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 声明上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 3. 设置 java.awt.headless 属性
configureHeadlessProperty();
// 4. 启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 5. 准备应用环境配置
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 6. 打印 Banner
Banner printedBanner = printBanner(environment);
// 7. 创建应用上下文 (ApplicationContext)
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 8. 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 9. 刷新应用上下文(容器)
refreshContext(context);
// 10. 刷新上下文后处理
afterRefresh(context, applicationArguments);
// 11. 应用启动计时结束
stopWatch.stop();
// 12. 打印启动时间日志
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 13. 启动监听器的 started 阶段
listeners.started(context);
// 14. 调用 CommandLineRunner 和 ApplicationRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 15 异常处理
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 16. 发布上下文就绪事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
1. 应用启动计时开始
StopWatch
是Spring 中的一个工具类,主要用于测量代码执行的时间,它能够帮助我们进行性能分析和调试。它的 start()
会开始计时,stop()
方法则会停止计时并计算时间差。
在这里是用来记录应用启动的时间,以便后续日志中打印启动时间。
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.currentTaskName = taskName;
this.startTimeNanos = System.nanoTime();
}
2. 声明上下文
bootstrapContext
包含一些初步的上下文信息和启动时的配置信息。createBootstrapContext()
负责初始化并返回一个 DefaultBootstrapContext
实例:
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
return bootstrapContext;
}
DefaultBootstrapContext 主要用于存储 Spring Boot 启动期间的配置信息。
这里是迭代 bootstrappers 调用其 intitialize()
进行初始化。Bootstrapper 是 Spring 中的一个引导器,主要作用是为框架的启动过程提供一种可扩展、模块化的初始化逻辑,定义如下:
public interface Bootstrapper {
void intitialize(BootstrapRegistry registry);
}
所以,在实际应用中,如果我们需要自定义一些组件,那么可以继承 Bootstrapper 接口来自定义初始化逻辑。
3. 设置 java.awt.headless 属性
headless
,即无头模式。Headless
模式是系统的一种配置模式,它是指运行 Java 程序时不需要与显示器、键盘或鼠标进行交互的模式。该属性在服务器环境中非常重要,因为很多服务器可能没有 GUI 环境。
这里默认为 true,可以确保程序以兼容无头模式的方式运行,避免 GUI 相关问题:
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
private boolean headless = true;