springboot启动流程原理

89 阅读3分钟

springboot的启动流程主要包括以下部分:
在这里插入图片描述

一、SpringBootApplication启动类

启动类的常见写法

@SpringBootApplication
public class SpringmvcApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringmvcApplication.class, args);
    }
}

需要重点关注的是@SpringBootApplication注解和SpringApplication.run(SpringmvcApplication.class, args)方法。

二、SpringApplication.run方法

1.实例化SpringApplication

SpringApplication.run(SpringmvcApplication.class, args)调用的方法;

//启动类调的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    //调的是下面的,参数是数组的run方法
    return run(new Class<?>[] { primarySource }, args);
}

//和上面的方法区别在于第一个参数是一个数组
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    //实际上new一个SpringApplication实例,调的是一个实例方法run()
    return new SpringApplication(primarySources).run(args);
}

判断当前项目类型

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    //断言primarySources不能为null,如果为null,抛出异常提示
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //启动类传入的Class
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //判断当前项目类型,有三种:NONE、SERVLET、REACTIVE
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //设置ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //判断主类,初始化入口类
    this.mainApplicationClass = deduceMainApplicationClass();
}

//判断主类
private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

设置初始化器
从类路径中META-INF/spring.factories加载对应的配置文件,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。

//设置初始化器(Initializer),最后会调用这些初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));

//设置初始化器(Initializer),最后会调用这些初始化器
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

// 这里的入参type就是ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 使用Set保存names来避免重复元素
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 根据names来进行实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    // 对实例进行排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

// 这里的入参type就是ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 使用Set保存names来避免重复元素
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 根据names来进行实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    // 对实例进行排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

设置监听器类。 主要的方式和设置初始化类一样。

整个流程:
在这里插入图片描述

总结

1、构造SpringApplication对象

主要判断容器类型是否为servlet、reactive还是none

2、获取ApplicationContextInitializer对象

从META-INF/spring.factories获取容器初始化需要的初始化对象

3、获取ApplicationListener对象

从META-INF/spring.factories获取监听器的配置类

2.执行run()方法

实例化后就是执行run()方法;

主要包括以下步骤:

  • 第一步:获取并启动监听器

  • 第二步:根据SpringApplicationRunListeners以及参数来准备环境

  • 创建Environment环境变量

    • 会查询系统变量,环境变量还有启动参数
  • 第三步:创建Spring容器

  • 第四步:Spring容器前置处理

  • 第五步:刷新容器

    • 将启动类作为配置类传给run()方法,作为spring容器的配置类
  • 第六步:Spring容器后置处理

  • 第七步:发出结束执行的事件

  • 第八步:执行Runners

    • 在springboot的启动过程中会执行ApplicationRunner和CommandLineRunner类型的bean

      1.获取Spring容器中的ApplicationRunner类型的Bean

      2.获取Spring容器中的CommandLineRunner类型的Bean

      3.执行它们的run()

会在springcontext创建之前就包environment环境变量准备好;

public ConfigurableApplicationContext run(String... args) {
    // 计时工具
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

    configureHeadlessProperty();

    // 第一步:获取并启动监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 第二步:根据SpringApplicationRunListeners以及参数来准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
        configureIgnoreBeanInfo(environment);

        // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
        Banner printedBanner = printBanner(environment);

        // 第三步:创建Spring容器
        context = createApplicationContext();

        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);

        // 第四步:Spring容器前置处理
        prepareContext(context, environment, listeners, applicationArguments,printedBanner);

        // 第五步:刷新容器
        refreshContext(context);
     // 第六步:Spring容器后置处理
        afterRefresh(context, applicationArguments);

      // 第七步:发出结束执行的事件
        listeners.started(context);
        // 第八步:执行Runners
        this.callRunners(context, applicationArguments);
        stopWatch.stop();
        // 返回容器
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, exceptionReporters, ex);
        throw new IllegalStateException(ex);
    }
}

3.项目启动可选的初始化操作

  • 通过ApplicationRunner和CommandLineRunner
  • 通过InitializingBean注解
  • 通过监听事件来处理

参考资料

  1. SpringBoot启动流程是怎样的?:juejin.cn/post/689534…
  2. SpringBoot 源码解析 (二)----- Spring Boot精髓:启动流程源码分析:www.cnblogs.com/java-chen-h…

todo

  1. 文章细节待补充待优化