[SpringBoot]启动流程详解:从入口到应用的时间线分析

501 阅读4分钟

SpringBoot启动流程详解:从入口到应用的时间线分析

SpringBoot 的启动流程是一个复杂但有序的过程,它从 main 方法开始,逐步完成环境准备、Bean 加载、自动配置、组件扫描等一系列操作。本文将用通俗易懂的语言和形象的代码例子,详细讲解 SpringBoot 的启动流程,并明确每个关键步骤的时间线,尤其是 @EnableAutoConfiguration@ComponentScan 的执行时机。


1. SpringBoot 启动的入口

SpringBoot 应用的启动入口通常是一个带有 main 方法的类,并且这个类上会标注 @SpringBootApplication 注解。这个注解是 SpringBoot 的核心注解,它组合了 @Configuration@EnableAutoConfiguration@ComponentScan 三个注解的功能。

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

1.1 SpringApplication.run 方法

SpringApplication.run 是 SpringBoot 启动的核心方法。它会创建一个 SpringApplication 实例,并调用其 run 方法。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return new SpringApplication(primarySource).run(args);
}

2. SpringBoot 启动的时间线

SpringBoot 的启动流程可以分为以下几个关键阶段,每个阶段都有明确的任务和时间线:

  1. 初始化阶段:创建 SpringApplication 实例,加载初始化和监听器。
  2. 环境准备阶段:加载配置文件,设置环境变量。
  3. 应用上下文创建阶段:根据应用类型创建 ApplicationContext
  4. Bean 加载阶段:执行 @ComponentScan,扫描并注册 Bean。
  5. 自动配置阶段:执行 @EnableAutoConfiguration,加载自动配置类。
  6. 应用启动完成阶段:执行 ApplicationRunnerCommandLineRunner

下面我们将详细分析每个阶段。


2.1 初始化阶段

SpringApplication 的构造函数中,SpringBoot 会进行一系列的初始化操作。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}
  • 推断应用类型:通过 WebApplicationType.deduceFromClasspath() 方法,SpringBoot 会根据类路径下的依赖推断应用类型(如 Servlet、Reactive 或非 Web 应用)。
  • 加载初始化和监听器:通过 getSpringFactoriesInstances 方法,SpringBoot 会从 META-INF/spring.factories 文件中加载 ApplicationContextInitializerApplicationListener 的实现类。

2.2 环境准备阶段

run 方法中,SpringBoot 会调用 prepareEnvironment 方法,加载配置文件并设置环境变量。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    return environment;
}
  • 加载配置文件:SpringBoot 会加载 application.propertiesapplication.yml 文件,并将其中的配置项注入到环境中。
  • 触发监听器listeners.environmentPrepared(environment) 会触发 ApplicationListener,通知环境准备完成。

2.3 应用上下文创建阶段

createApplicationContext 方法中,SpringBoot 会根据应用类型创建不同的 ApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        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);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
  • 创建上下文:对于 Web 应用,SpringBoot 会创建 AnnotationConfigServletWebServerApplicationContext;对于非 Web 应用,会创建 AnnotationConfigApplicationContext

2.4 Bean 加载阶段

refreshContext 方法中,SpringBoot 会调用 AbstractApplicationContextrefresh 方法,这是 Spring 容器初始化的核心方法。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        postProcessBeanFactory(beanFactory);
        invokeBeanFactoryPostProcessors(beanFactory);
        registerBeanPostProcessors(beanFactory);
        initMessageSource();
        initApplicationEventMulticaster();
        onRefresh();
        registerListeners();
        finishBeanFactoryInitialization(beanFactory);
        finishRefresh();
    }
}
  • 执行 @ComponentScan:在 invokeBeanFactoryPostProcessors 方法中,SpringBoot 会调用 ConfigurationClassPostProcessor,它会解析 @ComponentScan 注解,扫描指定包下的组件(如 @Component@Service@Repository 等),并将它们注册为 Bean。
@ComponentScan(basePackages = "com.example")
public class MyApplication {
    // ...
}

2.5 自动配置阶段

invokeBeanFactoryPostProcessors 方法中,SpringBoot 还会处理 @EnableAutoConfiguration 注解。

  • 加载自动配置类:SpringBoot 会从 META-INF/spring.factories 文件中加载所有自动配置类(如 DataSourceAutoConfigurationWebMvcAutoConfiguration 等),并根据条件决定是否启用它们。
@EnableAutoConfiguration
public class MyApplication {
    // ...
}
  • 条件化加载:自动配置类通常使用 @Conditional 注解(如 @ConditionalOnClass@ConditionalOnMissingBean)来决定是否生效。

2.6 应用启动完成阶段

refreshContext 方法完成后,SpringBoot 会执行 callRunners 方法,调用所有实现了 ApplicationRunnerCommandLineRunner 接口的 Bean。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    for (Object runner : runners) {
        if (runner instanceof ApplicationRunner) {
            ((ApplicationRunner) runner).run(args);
        }
        if (runner instanceof CommandLineRunner) {
            ((CommandLineRunner) runner).run(args);
        }
    }
}
  • 执行 Runner:这些 Runner 可以在应用启动后执行一些自定义的逻辑。

3. 总结

SpringBoot 的启动流程是一个有序的过程,每个阶段都有明确的任务和时间线。以下是关键步骤的总结:

  1. 初始化阶段:创建 SpringApplication 实例,加载初始化和监听器。
  2. 环境准备阶段:加载配置文件,设置环境变量。
  3. 应用上下文创建阶段:根据应用类型创建 ApplicationContext
  4. Bean 加载阶段:执行 @ComponentScan,扫描并注册 Bean。
  5. 自动配置阶段:执行 @EnableAutoConfiguration,加载自动配置类。
  6. 应用启动完成阶段:执行 ApplicationRunnerCommandLineRunner

通过本文的讲解,希望你能对 SpringBoot 的启动流程有更深刻的理解。如果你有任何问题或建议,欢迎在评论区留言讨论!


参考文献: