全网最详细的介绍SpringBoot启动过程源码分析

简介: 上一篇我们介绍了SpringBoot的自动装配的知识,这一篇我们将介绍SpringBoot最核心的知识点,SpringBoot应用的启动过程。这个启动过程比较复杂,在此我只介绍核心的知识点。其启动过程大概分为两步。1. 初始化SpringApplication对象,2.执行SpringApplication对象的run方法。

概述

上一篇我们介绍了SpringBoot的自动装配的知识,这一篇我们将介绍SpringBoot最核心的知识点,SpringBoot应用的启动过程。这个启动过程比较复杂,在此我只介绍核心的知识点。其启动过程大概分为两步。1. 初始化SpringApplication对象,2.执行SpringApplication对象的run方法。

SpringBoot启动流程图(以SpringBoot 1.5.8.RELEASE为例)

4d9c6a8cf10500c458fe66b0f663ef80_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

那我们就根据上面的启动流程图进行分析。

初始化SpingApplication对象

我们直接找到初始化SpingApplication对象的initialize方法。

private void initialize(Object[] sources) {
  if (sources != null && sources.length > 0) {
    this.sources.addAll(Arrays.asList(sources));
  }
  //检查当前环境是否是web环境
  this.webEnvironment = deduceWebEnvironment();
  //初始化ApplicationContextInitializer的实现类
  setInitializers((Collection) getSpringFactoriesInstances(
    ApplicationContextInitializer.class));
  //初始化ApplicationListener的实现类
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = deduceMainApplicationClass();
  }

如上初始化SpringApplication对象,主要的步骤有两步


1.加载spring.factories中ApplicationContextInitializer的配置类

2.加载spring.factories中ApplicationListener的配置类

都是通过SpringFactoriesLoader找到META-INF/spring.factories文件下配置了ApplicationContextInitializer和ApplicationListener两个接口的实现类,并且进行实例化。

cc5f9cb1da62ebdff734c8fe5e41a3d6_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70#pic_center.png

其中ApplicationContextInitializer接口主要目的是ConfigurableApplicationContext做refresh之前,对ConfigurableApplicationContext实例做进一步的设置或处理。如下图所示:

protected void applyInitializers(ConfigurableApplicationContext context) {
  for (ApplicationContextInitializer initializer : getInitializers()) {
    Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
      initializer.getClass(), ApplicationContextInitializer.class);
    Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    initializer.initialize(context);
  }
  }

而ApplicationListener则是一个监听器,他是Spring框架对Java事件监听机制的⼀种框架实现。

执行Run方法

说完了初始化SpingApplication对象的过程,接下来让我们看看run()方法的执行逻辑。

public ConfigurableApplicationContext run(String... args) {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  FailureAnalyzers analyzers = null;
  //1
        SpringApplicationRunListeners listeners = getRunListeners(args);
  listeners.starting();
  try {
    //2
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
      args);
    ConfigurableEnvironment environment = prepareEnvironment(listeners,
      applicationArguments);
    //3
    Banner printedBanner = printBanner(environment);
    //4
    context = createApplicationContext();
    //5
    analyzers = new FailureAnalyzers(context);
    //6
    prepareContext(context, environment, listeners, applicationArguments,
      printedBanner);
    //7
    refreshContext(context);
    //8
    afterRefresh(context, applicationArguments);
    //9
    listeners.finished(context, null);
    stopWatch.stop();
    return context;
  }
  catch (Throwable ex) {
    handleRunFailure(context, listeners, analyzers, ex);
    throw new IllegalStateException(ex);
  }

如上,就是执行run方法的主要逻辑,主要分为9个步骤。

1. 加载SpringApplicationRunListeners

首先第一步是:通过SpringFactoriesLoader 到META-INF/spring.factories查找并加载所有的SpringApplicationRunListeners,通过start()方法通知所有的SpringApplicationRunListener,本质上这是一个事件发布者,他在SpringBoot应用启动的不同阶段会发布不同的事件类型。SpringApplicationRunListener接口只有一个实现类EventPublishingRunListener,也就是说SpringApplicationRunListeners类的List<SpringApplicationRunListener> listeners中只会生成一个EventPublishingRunListener实例。那么SpringApplicationRunListeners是如何发布事件类型的呢?首先我们看下SpringApplicationRunListener这个接口。

public interface SpringApplicationRunListener {
  /**
  * run方法刚执行时通知
  */
  void starting();
  /**
  * Called once the environment has been prepared, but before the
  * {@link ApplicationContext} has been created.
  Environment准备好,ApplicationContext被创建好之前通知
  */
  void environmentPrepared(ConfigurableEnvironment environment);
  /**
  * Called once the {@link ApplicationContext} has been created and prepared, but
  * before sources have been loaded.
  ApplicationContext被创建好之后,但是资源加载好之前通知
  */
  void contextPrepared(ConfigurableApplicationContext context);
  /**
  * Called once the application context has been loaded but before it has been
  * refreshed.
ApplicationContext被加载好之后,但是没有被刷新之前通知
  */
  void contextLoaded(ConfigurableApplicationContext context);
  /**
  * Called immediately before the run method finishes.
  * @param context the application context or null if a failure occurred before the
  * context was created
  应用启动完成之后通知
  */
  void finished(ConfigurableApplicationContext context, Throwable exception);
}

如上我们看到SpringApplicationRunListener监听器SpringBoot应用启动的不同阶段都会有相应的监听通知。通知贯穿了SpringBoot应用启动的完成过程。我们以environmentPrepared通知为例看看,SpringApplicationRunListener是如何发布事件类型的,在其实现类EventPublishingRunListener中有属性名为initialMulticaster的SimpleApplicationEventMulticaster实例。在environmentPrepared方法中调用了SimpleApplicationEventMulticaster的multicastEvent方法,说明发布过程被委托给了SimpleApplicationEventMulticaster类,其中在multicastEvent方法中指定了相应的事件类型。

public void environmentPrepared(ConfigurableEnvironment environment) {
  this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
    this.application, this.args, environment));
  }

2. 创建并配置当前应用将要使用的环境

private ConfigurableEnvironment prepareEnvironment(
    SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments) {
  // 获取创建的环境,如果没有则创建,如果是web环境则创建StandardServletEnvironment
  ConfigurableEnvironment environment = getOrCreateEnvironment();
  //配置Environment:配置profile以及properties
  configureEnvironment(environment, applicationArguments.getSourceArgs());
  //调⽤SpringApplicationRunListener的 environmentPrepared()⽅法,通知事件监听者:应⽤的Environment已经准备好
  listeners.environmentPrepared(environment);
  if (!this.webEnvironment) {
    environment = new EnvironmentConverter(getClassLoader())
      .convertToStandardEnvironmentIfNecessary(environment);
  }
  return environment;
  }

第二步是创建并配置当前应用的环境(Environment),Environment用于描述应用程序当前的运行环境,其抽象了两方面的内容:1. 配置文件(profile)和属性(properties),我们知道不同的环境(开发环境,测试环境,发布环境)可以使用不同的属性配置,这些属性配置可以从配置文件,环境变量,命令行参数等来源获取。因此,当Environment准备好之后,在整个应用的任何时候,都可以获取这些属性。

所以,第二步的做的事情主要有如下三件:

1.获取创建的环境(Environment),如果没有则创建,如果是web环境则创建StandardServletEnvironment,如果不是的话则创建StandardEnvironment。

2.配置环境(Environment):主要是配置profile和属性properties。

3.调用SpringApplicationRunListener的environmentPrepared方法,通知事件监听者:应用环境(Environment)已经准备好了。

3.设置SpringBoot应用在启动时输出的Banner。

第三步是设置SpringBoot应用在启动时输出的Banner,默认的Banner如下图所示:

bdbb52284ea38118e92d43123ed96d1f_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

当然我们也可以修改默认的Banner,修改的方法就是在resources下新建一个banner.txt文件,替换掉默认的banner。

4. 根据是否是web项目,来创建不同的ApplicationContext容器

protected ConfigurableApplicationContext createApplicationContext() {
  Class<?> contextClass = this.applicationContextClass;
  if (contextClass == null) {
    contextClass = Class.forName(this.webEnvironment
      ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
    }
  }
  return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
  }

第四步是:创建不同的ApplicationContext容器;经过了前面的初始化SpingApplication对象的过程,我们就已经知道了当前应用的环境,那么如果是web应用,则创建AnnotationConfigEmbeddedWebApplicationContext对象,否则创建AnnotationConfigApplicationContext对象。

5. 创建一系列的FailureAnalyzer

FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
  this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader);
  this.analyzers = loadFailureAnalyzers(this.classLoader);
  prepareFailureAnalyzers(this.analyzers, context);
  }

第五步是创建FailureAnalyzer的代码如上所示:创建的流程依然是通过SpringFactoriesLoader获取所有的FailureAnalyzer接口的实现类名称,然后创建对应的实例。FailureAnalyzer的作用是用于分析故障并提供相关的诊断信息。

6. 初始化ApplicationContext

前面第四步,我们已经创建好了与本应用环境相匹配的ApplicationContext实例,那么第六步,就是对ApplicationContext进行初始化了。这一步也是比较核心的一步。首先让我们来看看实现逻辑的相关代码:

private void prepareContext(ConfigurableApplicationContext context,
    ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments, Banner printedBanner) {
  //1. 将准备好的Environment设置给ApplicationContext
  context.setEnvironment(environment);
  postProcessApplicationContext(context);
  //2. 遍历调用所有的ApplicationContextInitializer的  initialize()  方法来对已经创建好的 ApplicationContext 进行进一步的处理。
  applyInitializers(context);
  //3. 调用SpringApplicationRunListeners的 contextPrepared()  方法,通知所有的监听者,ApplicationContext已经准备完毕
  listeners.contextPrepared(context);
  //4. 将applicationArguments实例注入到IOC容器
  context.getBeanFactory().registerSingleton("springApplicationArguments",
    applicationArguments);
  if (printedBanner != null) {
    //5. 将printedBanner实例注入到IOC容器
    context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
  }
  //6. 加载资源,这里的资源一般是启动类xxxApplication
  Set<Object> sources = getSources();
  //7. 将所有的bean加载到容器中
  load(context, sources.toArray(new Object[sources.size()]));
  //8. 调⽤SpringApplicationRunListener的 contextLoaded()⽅法,通知所有的监听者:ApplicationContext已经装载完毕
  listeners.contextLoaded(context);
  }

如上就是初始化ApplicationContext的主要逻辑,主要有如下逻辑:

1.将准备好的Environment设置给ApplicationContext

2.遍历调用所有的ApplicationContextInitializer的 initialize() 方法来对已经创建好的 ApplicationContext 进行进一步的处理。

3.调用SpringApplicationRunListeners的 contextPrepared() 方法,通知所有的监听者,ApplicationContext已经准备完毕

4.将applicationArguments实例注入到IOC容器。

5.将printedBanner实例注入到IOC容器,这个就是第三步生成的Banner的实例。

6.加载资源,这里的资源一般是启动类xxxApplication

7.将所有的bean加载到容器中

8.调⽤SpringApplicationRunListeners的 contextLoaded()⽅法,通知所有的监听者:ApplicationContext已经装载完毕。

7. 调用ApplicationContext的refresh() 方法

第七步就是调用ApplicationContext的refresh() 方法,完成IOC容器的最后一道工序,为何要刷新容器呢?主要就是插手容器的启动。这里的 SpringApplication的 refresh方法最终还是调用到AbstractApplicationContext的refresh方法。

说到AbstractApplicationContext的refresh方法,就要回到我们前面说的Bean的生命周期。一个是BeanFactoryProcessor接口,用于插手容器的初始化。另外一个是BeanPostProcessor接口,用于插手Bean的实例化。

8.查找当前context中是否注册

查找当前context中是否注册有CommandLineRunner和ApplicationRunner,

如果有则遍历执行它们。

9.执行所有SpringApplicationRunListener的finished() 方法

对run方法的断点调试

1.5.8 版本的

2.1.3 版本的


总结

这就是Spring Boot的整个启动流程,其核⼼就是在Spring容器初始化并启动的基础上加⼊各种扩展点,这些扩展点包括:

ApplicationContextInitializer、ApplicationListener以及各种BeanFactoryPostProcessor等等。你对整个流程的细节不必太过关注,你只要理解这些扩展点是在何时如何⼯作的,能让它们为你所⽤即可。


相关文章
|
9月前
|
安全 Java Spring
SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)
SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)
123 0
|
9月前
|
设计模式 Java 容器
SpringBoot2 | SpringBoot启动流程源码分析(二)
SpringBoot2 | SpringBoot启动流程源码分析(二)
92 0
|
XML Java 数据格式
SpringBoot的启动过程
启动详解 SpringBoot的启动分为两个部分: 构造SpringApplication 执行run方法 构造SpringApplication 我们先来整体看看: 加入我们当前启动类如下: 可以发现大致做了以下几件事: 设置BeanDefinition的主源 推断应用类型 设置ApplicationContext 初始化器 设置监听器 推断著主启动类 接下来我们详细的看看每一个步骤: 第一步:记录 BeanDefinition 源 大家知道我们的Spring容器刚开始内部的BeanFactory是空的,它要从各个源头去寻找BeanDefinition, 这些源有可能来自
104 1
|
3月前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
64 3
|
6月前
|
设计模式 缓存 Java
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
|
8月前
|
监控 Java Spring
深入理解Spring Boot的启动过程
深入理解Spring Boot的启动过程
|
9月前
|
XML Java 开发者
springboot 启动原理、启动过程、启动机制的介绍
【5月更文挑战第13天】Spring Boot 是一种基于 Java 的框架,用于创建独立的、生产级别的 Spring 应用程序。它的主要目标是简化 Spring 应用的初始搭建和开发过程,同时提供一系列大型项目常见的非功能性特征(如嵌入式服务器、安全性、度量、健康检查和外部化配置)。
593 3
|
Java
如何在SpringBoot启动过程中,进行自定义操作?
如何在SpringBoot启动过程中,进行自定义操作?
84 0
|
9月前
|
前端开发 Java Spring
SpringBoot2 | SpringBoot Environment源码分析(四)
SpringBoot2 | SpringBoot Environment源码分析(四)
105 0
|
9月前
|
Java 中间件 容器
SpringBoot2 | SpringBoot启动流程源码分析(一)
SpringBoot2 | SpringBoot启动流程源码分析(一)
99 0

热门文章

最新文章