Springboot 启动流程分析

283 阅读5分钟

露从今夜白,月是故乡明。

1 前言

springboot 在java开发中是重要的框架,相较于 spring 来讲其开发方式更加的灵活、轻便。尤其是约定优于配置的理念极大了帮助了万千程序员更加专注于其业务实现而非繁杂的配置项。在本文中将以 springboot2.6.13 版本为基础,讲解其启动的核心流程。

2 启动入口

springboot 的入口通常是一个 main 方法,关键的技术点如下所示:

1754041597216.png

  • 1 引导类 AuthMpgApplication 作为配置源,通常包含 @SpringBootApplication 等其他配置项。
  • 2 参数传递,可以通过命令行参数传递给启动类,并配置启动参数和环境配置。

3 初始化阶段

1754125712204.png

在初始化阶段,主要构建以下四个内容:

  • 1 推断应用类型 WebApplicationType.deduceFromClasspath(), 检查类路径是否存在 DispatcherHandler(WebFlux),如果是则选择 REACTIVE 应用。如果类路径存在 ServletConfigurableWebApplicationContext, 则需要选择 Servlet 应用。否则就是普通应用 None

  • 2 加载系统中的引导类初始化器,即 BootstrapRegistryInitializer

  • 3 加载上下文的初始化器和事件监听器,需要加载 spring-bootspringboot-autoconfigureMETA-INF/spring.factoriesApplicationContextInitializerApplicationListener, 包含7个初始化器和8个监听器。初始化器用来执行自定义初始化逻辑,监听器用来监听启动过程中的各种事件。

  • 4 推断 deduceMainApplicationClass 应用的主类,用于读取配置,默认为当前的启动类。

此外在初始化阶段还可以设置类加载器、传入启动参数等。

4 运行阶段

1754137176205.png

run 方法是 springboot 启动的核心方法。

  • 1 首先会执行 createBootstrapContext 创建 BootstrapContext,在这个步骤中会主要是调用实现了 BootstrapRegistryInitializer 接口类的 initialize,在 springboot 中默认是没有实现的,在配置中心和服务发现组件中,会实现此类用于拉取远程配置。

  • 2 configureHeadlessProperty 方法主要是设置 java.awt.headless 变量,目的是为了在没有外部硬件键盘、显示器的情况下也能正常启动服务。

  • 3 获取系统中的所有监听器并启动监听,并发布应用开始启动的事件 ApplicationStartingEvent。启动的监听器即在初始化阶段从 srping.factories 中加载的 8个监听器。

  • 4 准备环境的应用参数,构建 ApplicationArguments 对象来存储环境变量。

  • 5 环境准备即 prepareEnvironment, 该方法是环境准备的核心。首先会根据初始化阶段的应用类型创建对应的 ConfigurableEnvironment 对象,加载系统的环境变量,jvm 信息,封装成为propertySources。接下来会按照系统传入的启动参数(包括命令行参数)加载系统中的配置文件,如 properties/yml 配置文件。环境准备好之后,会发布环境准备完成事件 ApplicationEnvironmentPreparedEvent,并将环境绑定到上下文 SpringApplication 中。

1754138415677.png

  • 6 执行打印 springboot banner 图,当然我们也可以自定义 banner 信息。

  • 7 创建应用上下文 createApplicationContext,在创建应上下文时,依旧要根据初始化环境时推断的 webApplicationType 来创建应用类型,默认是 Servlet 应用,这也是 springboot 嵌入式Web 容器的支持关键。即创建 AnnotationConfigServletWebServerApplicationContext 上下文对象。在该对象中,需要处理beanDefinition 信息以及注解的类信息,以及存放 bean 信息的工厂 DefaultListableBeanFactory。在这个阶段会处理注解后置处理器如下所示:

名称处理的注解
AutowiredAnnotationBeanPostProcessor@Autowired, @Value, @Inject
CommonAnnotationBeanPostProcessor@Resource, @PostConstruct, @PreDestroy
ConfigurationClassPostProcessor@Configuration, @Component, @Bean, @Import, @ImportResource, @PropertySource

1754139674299.png

  • 8 准备上下文信息 prepareContext。首先通过 postProcessApplicationContext 方法设置资源加载器、bean名称生成器以及类型转换器。接下来执行上下文初始化器,即准备阶段的7个初始化器,日志监听,容器ID、告警处理都是在这里进行处理。然后发布容器初始化完成事件 ApplicationContextInitializedEvent。获取上下文中的 BeanFactory 对象,注册启动参数、banner、是否允许循环依赖等信息,最后通过 load 方法将主类中的bean定义信息加载到 beandefinitionMap 中,接下来发布 ApplicationPreparedEvent 事件。

1754140595422.png

  • 9 刷新上下文 refreshContext,这里会调用 AbstractApplicationContext 中的 refresh 方法,也就是大家熟悉的自动装配流程,一共包含 12个步骤,这里的内容太重要后续会单独分享。

  • 10 刷新后置处理 afterRefresh ,通常留给子类进行实现,默认为空。

  • 11 发布应用启动完成事件,ApplicationStartedEvent 事件。

  • 12 执行回调方法,callRunners 方法,主要是执行实现了 CommandLineRunnerApplicationRunner 接口的类,用于回调使用。

  • 13 发布应用启动完成事件, ApplicationReadyEvent,接下来应用就可以接受访问了。

在整个启动的过程中,会遇到一些异常的情况,比如配置信息错误,找不到类信息等,会进入异常流程进行处理,并打印对应的异常堆栈,方便开发者发现并解决问题。

5 核心流程

springboot 的核心流程如下所示:

1754141862627.png

  • 1 在整个启动的过程中,贯穿了丰富的事件,驱动着容器的启动,并提供给开发者强大的扩展能力。
ApplicationStartingEvent 应用开始启动事件
ApplicationEnvironmentPreparedEvent 环境准备完成事件
ApplicationPreparedEvent 应用准备完成事件
ApplicationStartedEvent 应用启动完成事件
ApplicationReadyEvent 应用准备完成事件
  • 2 springboot 通过嵌入式推断应该启动的 web 容器,实现服务的一键启动。
  • 3 springboot 拥有灵活的扩展点,可以自定义实现初始化,监听器,事件以及启动完成后的回调操作。
  • 4 通过约定优于配置的契约,减少了传统 spring 开发的配置,并且可以通过配置灵活的实现自定义配置的切换。

6 总结

在本文中,主要分享了 springboot 的启动流程,着重讲述了其中的扩展点和启动的关键流程信息。在后续的工作中能够根据业务需要进行灵活的扩展功能,快捷、高效、简洁的实现业务功能,完成价值交付。