Spring Boot 启动流程深度解析:从源码到实践
Spring Boot 作为 Java 开发的主流框架,其 “约定大于配置” 的理念极大提升了开发效率。本文将从源码层面深入解析 Spring Boot 的启动流程,并通过代码示例展示其工作机制。
一、Spring Boot 启动的入口点
Spring Boot 应用的启动通常从一个包含main
方法的类开始:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication
是一个组合注解,包含:
@SpringBootConfiguration
:声明这是一个配置类@EnableAutoConfiguration
:启用自动装配机制@ComponentScan
:启用组件扫描
SpringApplication.run()
是启动的核心方法,下面我们深入分析其执行流程。
二、SpringApplication 的初始化
当调用SpringApplication.run()
时,首先会创建SpringApplication
实例:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
SpringApplication
的构造函数会执行一系列初始化操作:
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 判断应用类型(REACTIVE, SERVLET, NONE)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 设置ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3. 设置ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 4. 推断主应用类(包含main方法的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
关键步骤解析:
- 应用类型推断:根据类路径中的类推断应用类型(WebFlux、Servlet 或普通应用)
- 初始化器加载:从
META-INF/spring.factories
加载ApplicationContextInitializer
- 监听器加载:从
META-INF/spring.factories
加载ApplicationListener
- 主应用类推断:通过栈轨迹找到包含 main 方法的类
三、SpringApplication.run () 方法解析
run()
方法是 Spring Boot 启动的核心逻辑:
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 1. 获取并启动所有SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2. 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 3. 打印Banner
Banner printedBanner = printBanner(environment);
// 4. 创建ApplicationContext
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 5. 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 6. 刷新上下文
refreshContext(context);
// 7. 刷新后的处理
afterRefresh(context, applicationArguments);
// 8. 计时结束
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 9. 发布应用启动完成事件
listeners.started(context, timeTakenToStartup);
// 10. 调用所有Runners
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 11. 发布应用就绪事件
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
if (context != null) {
context.close();
throw new IllegalStateException("Error handling failed", ex);
}
throw new IllegalStateException(ex);
}
return context;
}
四、关键步骤详解
1. 启动监听器(SpringApplicationRunListeners)
Spring Boot 通过事件机制在启动的不同阶段发布事件,允许开发者介入:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
核心监听器包括EventPublishingRunListener
,它会发布一系列事件:
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationContextInitializedEvent
ApplicationPreparedEvent
ApplicationStartedEvent
ApplicationReadyEvent
2. 环境准备(prepareEnvironment)
环境准备包括创建并配置ConfigurableEnvironment
,加载属性源和配置文件:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext,
ApplicationArguments applicationArguments) {
// 创建环境(Web或非Web)
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 发布环境准备事件
listeners.environmentPrepared(bootstrapContext, environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
3. 创建应用上下文(createApplicationContext)
根据应用类型创建不同的ApplicationContext
:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据应用类型选择上下文类
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);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable to create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
4. 准备应用上下文(prepareContext)
对创建好的上下文进行初始化,包括设置环境、加载源、应用初始化器等:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置环境
context.setEnvironment(environment);
// 应用上下文后置处理器
postProcessApplicationContext(context);
// 应用初始化器
applyInitializers(context);
// 发布上下文初始化事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 添加资源加载器和主应用类
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置懒加载
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 加载源(主配置类)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// 发布上下文加载完成事件
listeners.contextLoaded(context);
}
5. 刷新应用上下文(refreshContext)
调用AbstractApplicationContext.refresh()
方法,这是 Spring 框架的核心逻辑,包括:
- BeanFactory 的创建与初始化
- BeanDefinition 的加载
- Bean 的创建与依赖注入
- 自动装配的处理
- 事件发布器的初始化
- 嵌入式 Servlet 容器的启动(如果是 Web 应用)
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
refresh(context);
}
protected void refresh(ConfigurableApplicationContext context) {
context.refresh();
}
6. 调用应用 Runner(callRunners)
启动完成后,调用所有实现了ApplicationRunner
或CommandLineRunner
的 Bean:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 添加所有ApplicationRunner
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 添加所有CommandLineRunner
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序
AnnotationAwareOrderComparator.sort(runners);
// 调用
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
五、自定义启动流程
开发者可以通过以下方式自定义启动流程:
1. 添加 ApplicationContextInitializer
public class CustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 在上下文刷新前执行自定义逻辑
System.out.println("Custom initializer called");
}
}
注册方式:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
app.addInitializers(new CustomApplicationContextInitializer());
app.run(args);
}
}
2. 添加 ApplicationListener
public class CustomApplicationListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 应用启动后执行
System.out.println("Application started!");
}
}
注册方式:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApplication.class);
app.addListeners(new CustomApplicationListener());
app.run(args);
}
}
3. 实现 CommandLineRunner 或 ApplicationRunner
@Component
public class CustomCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner executed with args: " + Arrays.toString(args));
}
}
六、启动流程总结
Spring Boot 的启动流程可以概括为以下关键步骤:
- 初始化 SpringApplication:
- 推断应用类型
- 加载初始化器和监听器
- 确定主应用类
- 执行 run () 方法:
- 发布启动事件
- 准备环境(加载配置属性)
- 创建应用上下文
- 准备上下文(设置环境、加载源)
- 刷新上下文(核心 Spring 容器初始化)
- 调用应用 Runner
- 发布就绪事件
- 核心机制:
- 事件机制:通过
ApplicationEvent
和ApplicationListener
实现 - 自动装配:在上下文刷新阶段通过
@EnableAutoConfiguration
触发 - 条件注解:控制配置类的加载条件
- 事件机制:通过
理解 Spring Boot 的启动流程有助于开发者更好地调试应用、自定义启动行为,以及解决启动过程中遇到的问题。通过扩展点(Initializers、Listeners、Runners),开发者可以在不修改核心代码的情况下介入启动流程,实现各种定制需求。