SpringBoot启动流程(一)

188 阅读4分钟

前言

我们今天来通过源码分析SpringBoot的启动流程,本文中所使用的SpringBoot的版本为2.7.12。

创建SpringApplication

在这里插入图片描述 进入run: 在这里插入图片描述

再次深入: 在这里插入图片描述我们发现这里创建了一个SpringApplication对象,从最上面图可以看出,primarySources即我们最开始传入的启动类即主方法类。 进入SpringApplication的创建:

在这里插入图片描述 我将SpringApplication的创建分为了四个关键步骤: 第一个步骤很简单,即对SpringApplication对象的资源加载器和主方法类以及一些其他属性进行了填充。

确定服务类型

我们来查看第二个步骤,进入该方法: 在这里插入图片描述 isPresent方法: 在这里插入图片描述 我们发现isPresent方法应该是通过类加载器对指定的类路径进行了加载,如果加载成功即加载的类存在加返回true,加载出现错误就返回false。 那么步骤二就是通过类加载的形式判断指定类是否存在来确定web服务的类型,即REACTIVE、NONE、SERVLET。

创建注册初始化、上下文初始化以及监听器

进入步骤三的两个set方法: 在这里插入图片描述 在这里插入图片描述 可以看出步骤三应该是创建了BootstrapRegistryInitializer、ApplicationContextInitializer和ApplicationListener即注册初始化、上下文初始化以及监听器,然后填充到了SpringApplication对象中。 但是它们是怎么被创建的呢?我们可以发现它们都存在了一个相同的方法,传入不同的类,然后通过这个方法返回创建的对象: 在这里插入图片描述 我们进入查看: 在这里插入图片描述 进入loadFactoryName方法: 在这里插入图片描述 继续进入: 在这里插入图片描述

spring.factory:

在这里插入图片描述

仔细阅读,我们可以发现这个方法其实并不难,其主要是加载META-INF/spring.factories这个文件,然后读取每一个键值对的信息,然后最后将这些键值对放入一个map集合中,最后放入本地的缓存集合中。需要获取时先走缓存,从缓存中以要获取的类为键从缓存中获取。

我们通过loadFactoryName方法获取了每一个要加载的类的路径,那么就需要通过这些路径加载这个类,并创建实例,即createSpringFactoriesInstances方法: 在这里插入图片描述 这个方法会通过路径加载这些类然后通过反射创建这些实例。

步骤三:加载META-INF/spring.factories文件中的注册初始化、上下文初始化以及监听器配置,并通过反射创建实例,填充到SpringApplication中

确定启动类本身

在这里插入图片描述 步骤四则会通过运行栈来获取main方法所在的类,即启动类本身

run方法

创建SpringApplication对象完成web服务的创建以及注册初始化、上下文初始化和监听器和其他属性的创建和填充后,就调用其run方法:

 public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        // 计时
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        // 设置系统Handless属性,表示没有显示器鼠标设备也会成功启动
        this.configureHeadlessProperty();
        // 创建运行时监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args); ...(1)
        // 向监听器发送启动事件
        listeners.starting();

        try {
            // 封装主方法传入的参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);   
            // 环境准备
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);...(2)
            this.configureIgnoreBeanInfo(environment);
            // 打印banner
            Banner printedBanner = this.printBanner(environment);
            // 创建应用上下文
            context = this.createApplicationContext();...(3)
            // 刷新应用上下文前的准备
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);...(4)
            // 刷新应用上下文
            this.refreshContext(context);...(5)
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, listeners);
            throw new IllegalStateException(var9);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var8) {
            this.handleRunFailure(context, var8, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var8);
        }
    }

我挑选几个重要的方法带大家来深入源码查看 进入(1): 在这里插入图片描述 查看运行时监听器的初始化方法: 在这里插入图片描述 其封装了日志和通过方法传入的监听器 查看获取监听器的方法: 在这里插入图片描述 在这里插入图片描述 我们可以发现这两个方法很熟悉,就是我们上面介绍的,从spring.factories文件中加载运行时监听器,并进行封装。

环境准备

进入(2): 在这里插入图片描述 查看2(1): 在这里插入图片描述 该方法会根据在第一步进行的SpringApplication的初始化时确定的web应用服务,来创建不同相应的环境,我们来查看默认Servlet服务下的环境: 在这里插入图片描述 可以看到其包含两个属性,进行debug查看环境: 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述

那么第一个方法其实就很简单理解了,其根据web服务创建相应的环境并填充了系统环境和JDK的环境信息。 查看第二个方法: 在这里插入图片描述 接着查看: 在这里插入图片描述 该方法将main方法传入的参数进行了封装并且设置到了环境中,如下图: 在这里插入图片描述 查看第二个方法: 在这里插入图片描述 该方法很简单,会确定当前的环境。 我们直接debug到最后,查看干了什么: 在这里插入图片描述 可以看到环境准备方法,添加了一些环境属性,并且其读取了配置文件application.yaml中的属性,对环境进行了设置

环境准备总结

环境准备阶段首先会根据web服务类型创建相应的环境,并且会填充相应的系统环境和JDK环境信息,以及填充主方法传入的参数,读取配置文件例如application.yaml等文件的属性进行填充。

创建应用上下文(容器)

进入(3): 在这里插入图片描述 在这里插入图片描述

这个方法相当简单,其会根据我们确认的web应用类型加载特定的上下文类,并且通过反射来创建应用上下文。