SpringBoot的启动过程

简介: SpringBoot的启动过程

正文


SpringBoot的启动过程


今天我们通过调试的方式讲述一下SpringBoot的启动的过程,加深自己的理解。


一、运行环境介绍


我们的运行环境是web的运行环境。

JDK:1.8

SpringBoot:2.2.2.RELEASE

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.breakpoint</groupId>
    <artifactId>spring-boot-learn</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--<dependency>-->
            <!--<groupId>org.springframework.boot</groupId>-->
            <!--<artifactId>spring-boot-starter-amqp</artifactId>-->
        <!--</dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--    属性绑定的适时候,会有响应的提示的    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    <!-- Package as an executable jar -->
    <!--  可以将这个应用打包成可以执行的jar  -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <!-- 配置成可以执行的jar -->
                    <executable>true</executable>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


二、启动的过程


2.1 创建SpringApplication对象并且执行run方法。


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

根据上面的执行的方式,可以总结为Spring启动的时候,一共做了2件事情:

1)、新建SpringApplication对象

2)、执行这个对象的run方法。

那么接下来分别看一看这2个方法都给咱们做了哪些的操作?


2.2 新建SpringApplication对象


新建SpringApplication对象是SpringBoot执行的第一个步骤:

// org.springframework.boot.SpringApplication#SpringApplication()
//
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 设置资源的加载对象
    this.resourceLoader = resourceLoader;
    //判断primarySources是不是null
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 设置主要资源的对象
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 决定当前应用的类别
    // 由于我们用的是web,所以当前的应用视为 WebApplicationType.SERVLET 是一个服务
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 从项目的META-INF/spring.factories文件里找到所有的ApplicationContextInitializer的组件,设置到this.initializers对象上
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 这个和上面的相似,将META-INF/spring.factories所有的ApplicationListener对象获取出来,设置到this.listeners这个对象上
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 决定哪一个类是祝启动类
      /*
          StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
          for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
              return Class.forName(stackTraceElement.getClassName());
            }
          }
    上面的方式用了一个抛出异常的方式来决定哪一个类的里面存在main方法。
      */
    this.mainApplicationClass = deduceMainApplicationClass();
}

决定当前的应用是web还是

8.png


2.3 执行run方法


下面的代码就是执行所有的run的方法。

public ConfigurableApplicationContext run(String... args) {
    // 创建一个监视器,监视整个项目的启动,执行的开始时间以及结束的时间
    StopWatch stopWatch = new StopWatch();
    // 开始监视
    stopWatch.start();
    // 生命ConfigurableApplicationContext对象,也就是IOC容器,后面有具体的创建的过程
    ConfigurableApplicationContext context = null;
    // 创建 exceptionReporters 、也就是异常报告对象,如果我们的应用在启动的过场中,出现了异常,这个时候,会报告相关的操作
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS 的信息
    configureHeadlessProperty();
    // 从 META-INF/spring.factories 获取到所有的SpringApplicationRunListener组件
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 这个listeners开始监听
    listeners.starting();
    try {
      // 设置应用的启动参数 也就是main防范的args参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 准备环境并且配置环境设置以及绑定到bindToSpringApplication上
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      // 配置环境
      configureIgnoreBeanInfo(environment);
      // 打印Banner 也就是springboot的图标
      Banner printedBanner = printBanner(environment);
      // 创建IOC容器对象,创建的过程中,根据不同的类别创建不同的IOC容器对象
      /*
        switch (this.webApplicationType) {
        case SERVLET:
          contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
          break;
        case REACTIVE:
          contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
          break;
        default:
          contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
        }
      */
      context = createApplicationContext();
      // 获取到错误的报告
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);
      //准备上下文环境
      // 1/IOC容器设置环境
      // 2.执行ApplicationContext 的后置处理器
      // 3.调用ApplicationContextInitializer 的initialize方法在refreshed之前
      // 4. 家在bean的定义到IOC容器
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      // 刷新IOC容器
      // 实际上就是调用Spring启动的核心的过程,调用各种的后置处理器以及初始化我们的bean的组件,并且将他们加入到IOC的容器中。
      // 这一步,也创建了enbedded的tomcat的对象,并且在我们的配置好的端口上启动了
      refreshContext(context);
      // 准备好IOC的容器后的操作
      afterRefresh(context, applicationArguments);
      // 停止应用的监控,实际上,我们的主要的应用已经启动完成啦
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      // SpringApplicationRunListener 的组件开始监听,也就是调用arted(context);的方法
      listeners.started(context);
      // 调用所有的Runners
      /*
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
          if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
          }
          if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
          }
        }
        根据上面的代码,可以知道 调用了所有的ApplicationRunner 以及 CommandLineRunner的组件,也就是最后的一次回调了。
      */
      callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
      // 如果有异常,那么就会报告异常
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
    }
    try {
      // 运行listeners.running
      listeners.running(context);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
    }
    return context;
  }


三、后记


经过上面的调试,更加的清晰的认识了SpringBoot的启动过程,SpringBoot还有其他许多的东西,比如自动装配等等这些比较重要的知识。

相关文章
|
7月前
|
XML Java 数据格式
SpringBoot的启动过程
启动详解 SpringBoot的启动分为两个部分: 构造SpringApplication 执行run方法 构造SpringApplication 我们先来整体看看: 加入我们当前启动类如下: 可以发现大致做了以下几件事: 设置BeanDefinition的主源 推断应用类型 设置ApplicationContext 初始化器 设置监听器 推断著主启动类 接下来我们详细的看看每一个步骤: 第一步:记录 BeanDefinition 源 大家知道我们的Spring容器刚开始内部的BeanFactory是空的,它要从各个源头去寻找BeanDefinition, 这些源有可能来自
69 1
|
4月前
|
Java 应用服务中间件 微服务
springboot详细启动过程
springboot详细启动过程
|
9月前
|
Java
如何在SpringBoot启动过程中,进行自定义操作?
如何在SpringBoot启动过程中,进行自定义操作?
41 0
|
9月前
|
Java 容器 Spring
|
9月前
|
搜索推荐 Java 应用服务中间件
SpringBoot(一):springboot应用程序启动过程核心分析
说起**springboot**大家很容易想到的就是**自动装配**、**约定大于配置**这个特点,的确这是springboot相比较于普通的**spring** web项目最大的亮点。
72 1
|
Java
SpringBoot项目启动过程中做数据资源初始化的方式
SpringBoot项目启动过程中做数据资源初始化的方式
367 0
|
Java 测试技术 容器
全网最详细的介绍SpringBoot启动过程源码分析
上一篇我们介绍了SpringBoot的自动装配的知识,这一篇我们将介绍SpringBoot最核心的知识点,SpringBoot应用的启动过程。这个启动过程比较复杂,在此我只介绍核心的知识点。其启动过程大概分为两步。1. 初始化SpringApplication对象,2.执行SpringApplication对象的run方法。
124 0
全网最详细的介绍SpringBoot启动过程源码分析
|
XML JSON 开发框架
关于SpringBoot的自动配置和启动过程
关于SpringBoot的自动配置和启动过程
关于SpringBoot的自动配置和启动过程
|
Java 应用服务中间件 Spring
SpringBoot内嵌tomcat启动过程
SpringBoot内嵌tomcat启动过程
176 0
|
XML Java 应用服务中间件
SpringBoot 启动过程 SpringApplication.run
SpringBoot 启动过程 SpringApplication.run
152 0