一篇彻底弄透Springboot启动流程

组里的实习生妹妹讲了一次Springboot的启动,

讲着讲着就讲到Springbean的生命周期去了,

我心想坏了,

这妮子估计把SpringbootSpring的相关逻辑给混淆了,

这必须得给她治一治。


前言

本文会对Springboot启动流程进行详细分析。但是请注意,Springboot启动流程是Springboot的逻辑,请千万不要将Springboot启动流程相关逻辑与Spring的相关逻辑混在一起,比如把Springbean生命周期的逻辑混在Springboot启动流程中,那么整个体系就复杂且混乱了。

所以本文仅重点关注Springboot启动流程,涉及Spring的部分,会略作说明并跳过。

整体的一个结构图如下。

Springboot版本:2.4.1

正文

一. Springboot启动流程图及说明

如下是Springboot的一个启动流程图。

SpringApplication完成初始化后,就会调用SpringApplication对象的run() 方法,该方法就是Springboot启动的入口,也对应着全流程图中的开始。下面给出SpringApplication对象的run() 方法说明,如下所示。

 

java

复制代码

public ConfigurableApplicationContext run(String... args) { // 创建StopWatch,用于统计Springboot启动的耗时 StopWatch stopWatch = new StopWatch(); // 开始计时 stopWatch.start(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); // 获取运行时监听器 SpringApplicationRunListeners listeners = getRunListeners(args); // 调用运行时监听器的starting()方法 // 该方法需要在Springboot一启动时就调用,用于特别早期的初始化 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 获取args参数对象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 读取Springboot配置文件并创建Environment对象 // 这里创建的Environment对象实际为ConfigurableEnvironment ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); // 打印Banner图标 Banner printedBanner = printBanner(environment); // 创建ApplicationContext应用行下文,即创建容器 context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // 准备容器 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 初始化容器 refreshContext(context); afterRefresh(context, applicationArguments); // 停止计时 stopWatch.stop(); if (this.logStartupInfo) { // 打印启动耗时等信息 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 调用运行时监听器的started()方法 // 该方法需要在应用程序启动后,CommandLineRunners和ApplicationRunners被调用前执行 listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { // 调用运行时监听器的running()方法 // 该方法需要在SpringApplication的run()方法执行完之前被调用 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }

二. SpringApplication的初始化

通常,Springboot应用程序的启动类定义如下。

 

java

复制代码

@SpringBootApplication public class LearnStartApplication { public static void main(String[] args) { SpringApplication.run(LearnStartApplication.class, args); } }

SpringApplication的静态run() 方法一路跟进,会发现如下的实现。

 

java

复制代码

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }

也就是Springboot启动时会先创建SpringApplication,然后再通过SpringApplicationrun() 方法完成启动。所以下面分析一下SpringApplication的初始化逻辑,其构造方法如下所示。

 

java

复制代码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 设置源 // 通常Springboot的启动类就是源 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 推断并设置WEB应用程序类型 // 根据classpath下的类来推断 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 加载并设置Bootstrapper this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); // 加载并设置初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 加载并设置应用事件监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 推断并设置应用程序主类的Class对象 this.mainApplicationClass = deduceMainApplicationClass(); }

梳理一下在SpringApplication的构造方法中,做了如下事情。

  1. 设置源。通常,Springboot中的源就是Springboot的启动类;
  2. 设置WEB应用程序类型。通过判断classpath下是否存在某些类,来推断当前WEB应用程序的类型;
  3. 加载并设置BootstrapperApplicationContextInitializerApplicationListener。借助SpringFactoriesLoader基于SPI机制完成BootstrapperApplicationContextInitializerApplicationListener的加载,然后设置到SpringApplication中;
  4. 设置应用程序主类的Class对象。

下面对上述事情进行分析。

1. 设置源

这里的源,也就是Spring容器启动时依赖的初始配置类,在Springboot中,初始配置类通常为启动类。下面可以通过调试看一下primarySources字段的值,如下所示。

可见源就是Springboot的启动类的Class对象。

2. 设置WEB应用程序类型

WebApplicationType#deduceFromClasspath方法如下所示。

 

java

复制代码

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; privat

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值