2024-12-16  阅读(7)
版权声明:本文为博主付费文章,严禁任何形式的转载和摘抄,维权必究。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1925060409

回答

Spring Boot 的 main() 是整个 Spring Boot 项目的启动入口。它通过 SpringApplication.run() 完成了整个项目的启动过程,最终让一个 Web 项目运行起来。

Spring Boot 的启动过程分为以下几个主要步骤:

  • 初始化 SpringApplicationSpringApplication 类在启动时会做一些配置工作,比如确定项目是否是 Web 应用。
  • 准备环境:Spring Boot 会加载运行时环境,比如系统属性、环境变量等,最终形成一个 Environment 对象。
  • 创建并刷新 Spring 容器:加载所有的 Bean 定义,并完成 Bean 的初始化工作。
  • 启动嵌入式 Web 容器:如果是 Web 项目,它会启动内嵌的 TomcatJetty 等 Web 服务器。
  • 运行应用程序:执行用户自定义的逻辑,比如 @SpringBootApplication 中的 CommandLineRunnerApplicationRunner

通过这些步骤,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;