问题
SpringBoot的启动流程是怎么样的?
答案
1. 核心概念
SpringBoot启动流程是从 main 方法调用 SpringApplication.run() 开始,经过环境准备、上下文创建、自动配置加载、Bean初始化等阶段,最终启动内嵌容器完成应用启动。
2. 完整启动流程详解
第一阶段:SpringApplication实例化
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
run方法内部实现:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return new SpringApplication(primarySource).run(args);
}
实例化过程(构造器):
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推断应用类型(SERVLET、REACTIVE、NONE)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 加载ApplicationContextInitializer(从spring.factories)
setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3. 加载ApplicationListener(从spring.factories)
setListeners(getSpringFactoriesInstances(ApplicationListener.class));
// 4. 推断主启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
第二阶段:执行run方法
public ConfigurableApplicationContext run(String... args) {
// 1. 创建StopWatch,用于统计启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 创建启动上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 3. 配置Headless模式(适用于无图形界面的服务器环境)
configureHeadlessProperty();
// 4. 获取并启动SpringApplicationRunListeners(事件发布)
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 5. 封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 6. 准备环境(Environment)
ConfigurableEnvironment environment = prepareEnvironment(listeners,
bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 7. 打印Banner
Banner printedBanner = printBanner(environment);
// 8. 创建ApplicationContext(根据应用类型)
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 9. 准备上下文
prepareContext(bootstrapContext, context, environment, listeners,
applicationArguments, printedBanner);
// 10. 刷新上下文(核心)
refreshContext(context);
// 11. 刷新后处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
// 12. 发布启动完成事件
listeners.started(context);
// 13. 执行Runners(CommandLineRunner、ApplicationRunner)
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 14. 发布就绪事件
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
3. 关键步骤详解
(1) 环境准备(prepareEnvironment)
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext,
ApplicationArguments applicationArguments) {
// 创建Environment对象(根据应用类型)
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置Environment(添加命令行参数、激活profile等)
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 绑定Environment到SpringApplication
ConfigurationPropertySources.attach(environment);
// 发布环境准备完成事件
listeners.environmentPrepared(bootstrapContext, environment);
return environment;
}
(2) 创建ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
// 根据应用类型创建不同的Context
// SERVLET -> AnnotationConfigServletWebServerApplicationContext
// REACTIVE -> AnnotationConfigReactiveWebServerApplicationContext
// NONE -> AnnotationConfigApplicationContext
(3) 准备上下文(prepareContext)
private void prepareContext(DefaultBootstrapContext bootstrapContext,
ConfigurableApplicationContext context,
ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments,
Banner printedBanner) {
// 设置环境
context.setEnvironment(environment);
// 后置处理ApplicationContext
postProcessApplicationContext(context);
// 应用初始化器(ApplicationContextInitializer)
applyInitializers(context);
// 发布上下文准备完成事件
listeners.contextPrepared(context);
// 注册启动参数Bean
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
// 加载主启动类
Set<Object> sources = getAllSources();
load(context, sources.toArray(new Object[0]));
// 发布上下文加载完成事件
listeners.contextLoaded(context);
}
(4) 刷新上下文(refreshContext)
这是Spring容器启动的核心,底层调用 AbstractApplicationContext.refresh():
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备刷新
prepareRefresh();
// 2. 获取BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. BeanFactory后置处理
postProcessBeanFactory(beanFactory);
// 5. 执行BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 刷新子类特定的Bean(如启动内嵌Tomcat)
onRefresh();
// 10. 注册监听器
registerListeners();
// 11. 实例化所有非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新,发布ContextRefreshedEvent
finishRefresh();
}
// 异常处理省略...
}
}
内嵌Tomcat启动(onRefresh阶段):
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建并启动WebServer(Tomcat/Jetty/Undertow)
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
4. 启动流程时序图
main()
↓
new SpringApplication()
├─ 推断应用类型
├─ 加载初始化器
├─ 加载监听器
└─ 推断主类
↓
run()
├─ starting事件
├─ 准备Environment
├─ environmentPrepared事件
├─ 打印Banner
├─ 创建ApplicationContext
├─ 准备Context
│ ├─ 应用初始化器
│ ├─ contextPrepared事件
│ └─ contextLoaded事件
├─ 刷新Context
│ ├─ 自动配置加载
│ ├─ Bean实例化
│ └─ 启动内嵌容器
├─ started事件
├─ 执行Runners
└─ running事件
↓
应用启动完成
5. 性能优化考量
- 懒加载:配置
spring.main.lazy-initialization=true延迟Bean初始化 - 排除自动配置:排除不需要的AutoConfiguration减少启动时间
- Profile优化:根据环境按需加载配置
- Spring Native:使用GraalVM编译为原生镜像,启动时间从秒级降至毫秒级
6. 实战调试技巧
启用启动日志
# application.properties
debug=true
logging.level.org.springframework.boot=DEBUG
自定义监听器监控启动过程
@Component
public class StartupListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
System.out.println("应用正在启动...");
} else if (event instanceof ApplicationEnvironmentPreparedEvent) {
System.out.println("环境准备完成");
} else if (event instanceof ApplicationContextInitializedEvent) {
System.out.println("上下文初始化完成");
} else if (event instanceof ApplicationPreparedEvent) {
System.out.println("应用准备完成");
} else if (event instanceof ApplicationStartedEvent) {
System.out.println("应用启动完成");
} else if (event instanceof ApplicationReadyEvent) {
System.out.println("应用就绪");
}
}
}
7. 面试答题要点总结
完整流程概括:
- 实例化SpringApplication:推断应用类型、加载初始化器和监听器
- 准备环境:加载配置文件、激活profile
- 创建上下文:根据应用类型创建对应的ApplicationContext
- 准备上下文:应用初始化器、加载主配置类
- 刷新上下文:执行Spring容器的refresh流程,加载自动配置、实例化Bean、启动内嵌容器
- 执行Runners:调用CommandLineRunner和ApplicationRunner
- 发布就绪事件:应用完全启动,可对外提供服务
核心关键点:
SpringApplication.run()是入口refresh()是Spring容器启动的核心- 自动配置在
invokeBeanFactoryPostProcessors阶段加载 - 内嵌Tomcat在
onRefresh()阶段启动