SpringBoot超详细创建运行过程,一步步带你Debug

SpringApplication对象的创建

用过SpringBoot的都知道,在项目启动入口的主类main()方法就开启了SpringBoot的启动之路
在这里插入图片描述
在这里打上一个断点启动
在这里插入图片描述
可以发现它底层是先创建一个SpringApplication对象,然后调用run方法,点进其构造器可以发现

this.primarySources

在这里插入图片描述
除去上面的一些基本的赋值,方法来到这,可以见到它为主资源this.primarySources赋值,就是我们的主类

this.webApplicationType

点击进入deduceFromClasspath()方法
在这里插入图片描述
他会判断我们应用的类型进行返回赋值
在这里插入图片描述
本环境采用的是最纯净的环境,类型为None,在WEB开发中返回的类型一般是SERVLET

setInitializers

在这里插入图片描述
在这里插入图片描述
它是给自己的一个Initializers属性赋值,会去META-INF 目录下找spring.factories里的ApplicationContextInitializer,最终赋值给自己的属性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发现Spring-boot-2.6.3.jar下有5个,spring-boot-autoconfigure-2.6.3.jar下有两个,一共七个

setListeners

会去META-INF 目录下找spring.factories里的ApplicationListener,最终赋值给自己的属性listeners,和上个类似
在这里插入图片描述

调的是同一个方法,根据入参找类型
在这里插入图片描述
在这里插入图片描述

发现Spring-boot-2.6.3.jar下有7个,spring-boot-autoconfigure-2.6.3.jar下有1个,一共8个

this.mainApplicationClass

在这里插入图片描述
在这里插入图片描述
其大概逻辑就是找到第一个带有main()方法的程序,就是我们的主程序

小结

创建SpringApplication主要干了这些事情

  1. primarySources 保存当前主启动类信息
  2. webApplicationType 判断当前应用类型,web应用中一般为Servlet应用
  3. setInitializers 去spring.factories找ApplicationContextInitializer 然后赋值给属性Initializers
  4. setListeners 去spring.factories找ApplicationListener 然后赋值给属性listeners

SpringApplication运行过程

当SpringApplication对象创建完成后,就是调用其Run方法,run方法代码如下

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            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 var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

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

StopWatch.start()

进入到start方法

public void start() throws IllegalStateException {
        this.start("");
    }

可见其作用是纪录程序启动的当前时间

public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
            throw new IllegalStateException("Can't start StopWatch: it's already running");
        } else {
            this.currentTaskName = taskName;
            this.startTimeNanos = System.nanoTime();
        }
    }

configureHeadlessProperty()

在这里插入图片描述
让当前应用进入headless模式

getRunListeners(args)

在这里插入图片描述
相信大家对这个方法已经很熟悉了,去META-INF/spring.factories下找SpringApplicationRunListener,获取所有运行监听器RunListener

listeners.starting()

监听器通知项目正在启动中
在这里插入图片描述
不难看出,其是遍历通知

applicationArguments

保存命令行参数args
在这里插入图片描述

prepareEnvironment

在这里插入图片描述

getOrCreateEnvironment()

在这里插入图片描述
可以看见其为我们装载一个SERVLET环境

this.configureEnvironment

在这里插入图片描述
其为我们配置类型转换器
在这里插入图片描述
点进configurePropertySources方法
在这里插入图片描述
environment.getPropertySources();是得到所有PropertySources
在这里插入图片描述
返回上级配置激活Profile信息this.configureProfiles(environment, args);

listeners.environmentPrepared

在这里插入图片描述
清晰得知,遍历每一个监听器,通知我们的应用环境已经准备好了
在这里插入图片描述

创建IOC容器createApplicationContext

在这里插入图片描述
在这里插入图片描述
根据web类型创建一个IOC容器

准备IOC容器上下文prepareContext

在这里插入图片描述

context.setEnvironment(environment)

在这里插入图片描述

postProcessApplicationContext()

在这里插入图片描述

applyInitializers

在这里插入图片描述
getInitializers()就是我们在创建SpringApplication对象是存进去的ApplicationContextInitializer

listeners.contextPrepared()

在这里插入图片描述

在这里插入图片描述
也是遍历调用RunListener的contextPrepareds

getBeanFactory()

beanFactory.registerSingleton

在这里插入图片描述

arg参数在spring中也是个组件,组件名叫springApplicationArguments,注册到bean工厂中

listeners.contextLoaded();

在这里插入图片描述
老规矩 监听器调用contextload方法

refreshContext(核心源码)

作用是刷新IOC容器,创建容器中的所有组件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

step into ((AbstractApplicationContext)applicationContext).refresh()
在这里插入图片描述
如图所示,springboot组件以及其自动装配的流程都在这一步发生

afterRefresh

this.afterRefresh(context, applicationArguments);
容器刷新完成后工作

stopWatch.stop();

记录运行完成后的结束时间

listeners.started(context);

老朋友了,遍历listeners调用started方法

callRunners

在这里插入图片描述

  1. 获取容器中的ApplicationRunner
  2. 获取容器中的CommandLineRunner
  3. 合并所有runner并按照@Order注解进行排序(越小 优先级越高)
  4. 遍历所有的runner,调用run方法,先A后C

handleRunFailure

如果有异常,遍历调用listener的Failed方法
在这里插入图片描述

listeners.running(context)

调用所有监听器的run方法,如果有异常则调用failed方法

return context

返回IOC容器

总结

  • StopWatch记录应用的启动时间
  • 让当前应用进入到headless模式。java.awt.headless
  • 获取所有的RunListener(运行监视器)【为了方便所有监视器进行事件感知】getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
  • 遍历 SpringApplicationRunListener 调用 starting 方法;相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
  • 保存命令行参数;ApplicationArguments。
  • 准备环境 prepareEnvironment()
    • 返回或者创建基础环境信息对象。StandardServletEnvironment
    • 读取所有的配置源的配置属性值。得到所有的PropertySources,@PropertySources即加载外部所有的配置文件xxx.properties
    • 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
  • 创建IOC容器(createApplicationContext())
    • 根据项目类型(Servlet)创建容器
    • 当前会创建 AnnotationConfigServletWebServerApplicationContext
  • 准备ApplicationContext IOC容器的基本信息 prepareContext()
    • 保存环境信息
    • IOC容器的后置处理流程
    • 应用初始化器;applyInitializers;
  • 遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
  • 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器contextPrepared
    • 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded
  • 刷新IOC容器 refreshContext【核心源码
    • 创建容器中的所有组件(Spring注解)
  • 容器刷新完成后工作?afterRefresh
  • 所有监听器 调用 listeners.started(context); 通知所有的监听器 started
  • 调用所有runners;callRunners()
    • 获取容器中的ApplicationRunner
    • 获取容器中的 CommandLineRunner
    • 合并所有runner并且按照@Order进行排序
    • 遍历所有的runner。先ApplicationRunner 后CommandLineRunner调用 run 方法
  • 如果以上有异常,调用Listener的failed
  • 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
  • running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值