探秘SpringBoot启动流程:原理解析与自定义扩展

本文详细剖析了SpringBoot的启动过程,包括初始化步骤、关键方法和可扩展点,如ApplicationContextInitializer的应用以及如何自定义启动Banner。读者将对SpringBoot的工作原理有更深入的理解。
摘要由CSDN通过智能技术生成

引言

SpringBoot是当今Java开发中最受欢迎的微服务框架之一,其简化了Java应用的开发和部署过程。了解SpringBoot的启动流程对于深入理解其原理和内部机制至关重要。本文将深入分析SpringBoot的启动过程,探讨其中的关键步骤和机制,后基于这些机制,我们尝试做一些扩展和一些个性化内容。

启动流程分析

流程图

SpringBoot 启动流程

源代码

// org.springframework.boot.SpringApplication
public class SpringApplication {
  public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
  }
  public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      listeners.started(context, timeTakenToStartup);
      callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
    }
    try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
    }
    return context;
  }
}

初始化 SpringApplication 实例

在SpringBoot应用启动时,首先会初始化一个SpringApplication实例。在这个过程中,会进行一系列配置和准备工作,包括:

指定资源加载器和主类

通过指定资源加载器和主类(即ApplicationStarter类),SpringBoot能够正确地加载应用所需的资源,并确定应用的入口点。

指定Web应用类型

SpringBoot支持多种Web应用类型,包括无Web环境、Servlet环境和响应式环境。通过指定不同的应用类型,可以适配不同的部署场景。

获取系统初始化器和监听器

SpringBoot允许用户注册自定义的初始化器和监听器,这些组件可以在应用启动过程中执行特定的逻辑,如配置环境、加载资源等。

执行 SpringApplication#run 方法

一旦SpringApplication实例初始化完成,接下来就会执行run方法来启动SpringBoot应用。在这个过程中,会经历以下关键步骤:

创建启动器上下文

首先,会创建一个启动器上下文,用于管理应用的生命周期和组件。

获取监听器集合

获取所有注册的SpringApplicationRunListener,并通知它们应用即将启动的事件。

处理环境相关事项

在启动过程中,会处理环境相关的配置,包括创建和配置ConfigurableEnvironment,并根据配置信息打印Banner。

创建和初始化应用上下文

这是整个启动过程的核心。首先,会创建一个ConfigurableApplicationContext,然后对其进行初始化。这个过程包括设置环境、注册单例Bean、初始化BeanFactory等。

启动应用上下文

一旦应用上下文初始化完成,就会启动应用上下文的生命周期,执行一系列初始化和准备工作。

通知监听器应用已启动

在应用启动完成后,会通知所有注册的监听器应用已经启动,可以执行相应的逻辑。

执行自定义逻辑

最后,会执行注册的ApplicationRunnerCommandLineRunnerrun方法,这些方法中可以执行一些自定义的初始化逻辑。

应用扩展示例

在启动流程分析中,我们展示了路程图,其中流程图中蓝色部分就是我们能在 SpringBoot 应用启动过程中的所有扩展点(ApplicationContext IoC 容器生命周期中也还有,如 BeanPostProcessor 等,不过我们这里先不关注)。

下面我们就举出一个上面列出的扩展示例,其他如果有兴趣的朋友也可自行尝试,非常简单。

ApplicationContextInitializer

ApplicationContextInitializer 是 Spring Framework 提供的一个接口,它允许我们在 Spring 应用程序上下文(ApplicationContext)创建之前对其进行自定义初始化。这意味着我们可以在 Spring 容器初始化之前执行一些操作,例如设置环境变量、配置属性等。

下面我们展示一下如何使用:

  • 定义一个 ApplicationContextInitializer 实现,如 MarkusApplicationContextInitializer。
public class MarkusApplicationContextInitializer implements ApplicationContextInitializer {
  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    System.out.println("MarkusApplicationContextInitializer");
    // 这里我们可以编写自定义的初始化逻辑
  }
}
  • 在 resources/ 目录下,创建一个 META-INF/spring.factories 文件,指定上我们的自定义实现。(这里就是 Spring 自己定义的 SPI 机制,框架可以自动发现自定义组件)
org.springframework.context.ApplicationContextInitializer=com.markus.spring.boot.extendsion.MarkusApplicationContextInitializer
  • 启动 SpringBoot
@SpringBootApplication
public class SpringApplicationStarter {
  /**
   * Spring Boot 启动时的几个扩展点:
   *  1. org.springframework.boot.SpringApplication#bootstrapRegistryInitializers
   *  2. org.springframework.boot.SpringApplication#initializers
   *  3. org.springframework.boot.SpringApplication#listeners
   *  4. org.springframework.boot.SpringApplication#callRunners(org.springframework.context.ApplicationContext, org.springframework.boot.ApplicationArguments)
   *
   */
  public static void main(String[] args) {
    SpringApplication.run(SpringApplicationStarter.class, args);
  }
}

我们看下效果:

image-20240303001455705

启动 Banner 修改

我们经常看到,在 SpringBoot 启动的时候,会打印如下图所示的 Banner:

image-20240303000610861

那么,我们现在分析完了 SpringBoot 的启动过程后,就知道我们可以自定扩展这个文案,怎么扩展呢?

非常简单,在 application.yml 文件里指定一下自定义 banner 的文件位置,SpringBoot 就可以打印我们指定的文案了。

自定义 banner

好了,现在我们在此启动 SpringBoot,图案就是我们自己设置的了:

image-20240303001031309

本文总结

总结一下,本文深入分析了SpringBoot的启动流程,重点关注了SpringApplication的初始化和启动过程。在初始化SpringApplication实例时,会指定资源加载器、主类以及Web应用类型,并获取系统初始化器和监听器。随后,通过run方法启动SpringBoot应用,其中包括创建启动器上下文、获取监听器集合、处理环境相关事项、创建和初始化应用上下文、启动应用上下文、通知监听器应用已启动等关键步骤。文章还展示了如何通过ApplicationContextInitializer进行应用扩展,以及如何自定义Banner。通过本文,大家可以深入了解SpringBoot的启动过程及其扩展方式,为更深入地学习和应用SpringBoot提供了指导和启示。

  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值