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 的启动流程可以分为以下几个关键阶段,每个阶段都有明确的任务和时间线:
- 初始化阶段:创建
SpringApplication实例,加载初始化和监听器。 - 环境准备阶段:加载配置文件,设置环境变量。
- 应用上下文创建阶段:根据应用类型创建
ApplicationContext。 - Bean 加载阶段:执行
@ComponentScan,扫描并注册 Bean。 - 自动配置阶段:执行
@EnableAutoConfiguration,加载自动配置类。 - 应用启动完成阶段:执行
ApplicationRunner和CommandLineRunner。
下面我们将详细分析每个阶段。
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文件中加载ApplicationContextInitializer和ApplicationListener的实现类。
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.properties或application.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 会调用 AbstractApplicationContext 的 refresh 方法,这是 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文件中加载所有自动配置类(如DataSourceAutoConfiguration、WebMvcAutoConfiguration等),并根据条件决定是否启用它们。
@EnableAutoConfiguration
public class MyApplication {
// ...
}
- 条件化加载:自动配置类通常使用
@Conditional注解(如@ConditionalOnClass、@ConditionalOnMissingBean)来决定是否生效。
2.6 应用启动完成阶段
在 refreshContext 方法完成后,SpringBoot 会执行 callRunners 方法,调用所有实现了 ApplicationRunner 或 CommandLineRunner 接口的 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 的启动流程是一个有序的过程,每个阶段都有明确的任务和时间线。以下是关键步骤的总结:
- 初始化阶段:创建
SpringApplication实例,加载初始化和监听器。 - 环境准备阶段:加载配置文件,设置环境变量。
- 应用上下文创建阶段:根据应用类型创建
ApplicationContext。 - Bean 加载阶段:执行
@ComponentScan,扫描并注册 Bean。 - 自动配置阶段:执行
@EnableAutoConfiguration,加载自动配置类。 - 应用启动完成阶段:执行
ApplicationRunner和CommandLineRunner。
通过本文的讲解,希望你能对 SpringBoot 的启动流程有更深刻的理解。如果你有任何问题或建议,欢迎在评论区留言讨论!
参考文献: