深入理解 SpringBoot 启动机制:run()启动源码全过程分析

本文深入解析SpringApplication的run()方法,从创建计时监控类、初始化应用上下文到准备环境、创建ApplicationContext,再到异常报告和监听器事件,详述SpringBoot启动全过程。
摘要由CSDN通过智能技术生成

一、前言
在上一篇我们了解到 new SpringApplication(primarySources)实例初始化源码的加载过程,通过走跟源码分析了基本初始化过程如下:

资源初始化资源加载器为 null

断言主要加载资源类不能为 null,否则报错

初始化主要加载资源类集合并去重

推断当前 WEB 应用类型

设置应用上下文初始化器

设置监听器

推断主入口应用类

如果,各位同学有遗忘的,可以去复习一下上篇文章深入理解SpringBoot核心原理:初始化流程(run方法)。

那么,这篇我们继续往下面分析其核心 run 方法。

二、SpringApplication 实例 run 方法运行过程
深入理解 SpringBoot 启动机制:run()启动源码全过程分析

下面继续来分析SpringApplication对象的run方法实现过程以及运行原理。

还是跟之前的分析流程一样,先来看一下run方法里面总体的流程实现:

public ConfigurableApplicationContext run(String… args) {
// 1、创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();

    // 2、初始化应用上下文和异常报告集合
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

    // 3、设置系统属性 `java.awt.headless` 的值,默认值为:true
    configureHeadlessProperty();

    // 4、创建所有 Spring 运行监听器并发布应用启动事件
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {

        // 5、初始化默认应用参数类
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 6、根据运行监听器和应用参数来准备 Spring 环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);

        // 7、创建 Banner 打印类
        Banner printedBanner = printBanner(environment);

        // 8、创建应用上下文
        context = createApplicationContext();

        // 9、准备异常报告器
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);

        // 10、准备应用上下文
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);

        // 11、刷新应用上下文
        refreshContext(context);

        // 12、应用上下文刷新后置处理
        afterRefresh(context, applicationArguments);

         // 13、停止计时监控类
        stopWatch.stop();

        // 14、输出日志记录执行主类名、时间信息
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }

        // 15、发布应用上下文启动完成事件
        listeners.started(context);

        // 16、执行所有 Runner 运行器
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {

        // 17、发布应用上下文就绪事件
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    // 18、返回应用上下文
    return context;
}

三、run 方法运行过程分解
3.1 创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();
进入start()方法如下:

/**
 * Start an unnamed task. The results are undefined if {@link #stop()}
 * or timing methods are called without invoking this method.
 * @see #stop()
 */
public void start() throws IllegalStateException {
    start("");
}

/**
 * Start a named task. The results are undefined if {@link #stop()}
 * or timing methods are called without invoking this method.
 * @param taskName the name of the task to start
 * @see #stop()
 */
public void start(String taskName) throws IllegalStateException {
    if (this.currentTaskName != null) {
        throw new IllegalStateException("Can't start StopWatch: it's already running");
    }
    this.currentTaskName = taskName;
    this.startTimeMillis = System.currentTimeMillis();
}

首先记录了当前任务的名称,默认为空字符串,然后记录当前 Spring Boot 应用启动的开始时间。

3.2 初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
3.3 设置系统属性 java.awt.headless 的值
configureHeadlessProperty();
至于为什么设置这个属性值为true,可以参考下面这篇文章:

https://www.cnblogs.com/princessd8251/p/4000016.html

3.4 创建所有 Spring 运行监听器并发布应用启动事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
进去看一下创建spring运行监听器的相关源码:

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

SpringApplicationRunListeners {

SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}

}
创建逻辑和之前实例化初始化器和监听器的一样,一样调用的是getSpringFactoriesInstances 方法来获取配置的监听器名称并实例化所有的类。

SpringApplicationRunListener所有监听器配置在 spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories 这个配置文件里面:

Run Listeners

org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener
3.5 初始化默认应用参数类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
3.6 根据运行监听器和应用参数来准备 Spring 环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
下面我们主要来看下准备环境的 prepareEnvironment 源码:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // 1.Create the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 2.Configure the environment
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

3.7 创建 Banner 打印类
Banner printedBanner = printBanner(environment);
3.8 创建应用上下文
context = createApplicationContext();
进去源码,可以知道这里主要是根据不同的应用类型初始化不同的上下文应用类。

3.9 准备异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
getSpringFactoriesInstances ------>>createSpringFactoriesInstances ------->>>逻辑和之前实例化初始化器和监听器的一样,一样调用的是 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。

该异常报告处理类配置在 spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories 这个配置文件里面。

Error Reporters

org.springframework.boot.SpringBootExceptionReporter=
org.springframework.boot.diagnostics.FailureAnalyzers
3.10 准备应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
接下来进入prepareContext方法:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    // 配置上下文的 bean 生成器及资源加载器
    postProcessApplicationContext(context);
    // 为上下文应用所有初始化器
    applyInitializers(context);
    // 触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
    listeners.contextPrepared(context);
    // 记录日志
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // Add boot specific singleton beans 启动两个特殊的单例bean
    context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    // Load the sources 加载所有资源
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    // 触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
    listeners.contextLoaded(context);
}

3.11 刷新应用上下文
refreshContext(context);
3.12 应用上下文刷新后,自定义处理
afterRefresh(context, applicationArguments);

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {

}
3.13 停止计时监控类
stopWatch.stop();
计时监听器停止,并统计一些任务执行信息。

3.14 输出日志记录执行主类名、时间信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);

}
3.15 发布应用上下文启动完成事件
listeners.started(context);
这里会触发所有 SpringApplicationRunListener 监听器的 started 事件方法。

3.16 执行所有 Runner 运行器
callRunners(context, applicationArguments);
执行所有ApplicationRunner以及CommandLineRunner执行器

3.17 发布应用上下文就绪事件
listeners.running(context);
触发所有 SpringApplicationRunListener 监听器的 running 事件方法。

3.18 返回应用上下文
return context;
四、总结
关于SpringBootApplication.run()启动实例初始化以及实例加载run方法的源码分析到此结束,分析源码是件有点痛苦的事情,不过分析完源码后,你会对SpringBoot是如何加载以及初始化有更全面的了解。

当然其中也有其它的一些东西值得学习,比如Spring事件监听,如何使用单例,自动化配置等等,最后,希望给各位同学在学习SpringBoot的路上提供一点帮助。

看完,如果觉得有收获,希望点个赞。
高质量编程视频shangyepingtai.xin

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值