Spring Boot启动流程的核心代码:
Java
public ConfigurableApplicationContext run(String... args) {
// 记录应用程序启动时间(纳秒)
long startTime = System.nanoTime();
// 创建默认的引导上下文(BootstrapContext),包含一些启动所需的基本信息和资源
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 初始化Spring应用上下文(ApplicationContext)变量,初始值为null
ConfigurableApplicationContext context = null;
// 配置无头(Headless)属性,如在没有图形界面的环境中运行
configureHeadlessProperty();
// 获取Spring应用启动监听器集合,用于在启动过程中触发相关事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 触发"starting"事件,通知所有监听器Spring应用启动开始
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 创建包含命令行参数的应用程序参数对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备应用环境(Environment),包括加载配置文件、设置系统属性等
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 打印启动时的Banner信息(如果有)
Banner printedBanner = printBanner(environment);
// 创建Spring应用上下文实例,具体实现取决于所选的ApplicationContext类型(如AnnotationConfigApplicationContext、XmlApplicationContext等)
context = createApplicationContext();
// 将应用启动追踪器(ApplicationStartup)设置到上下文中,用于记录启动过程中的耗时信息
context.setApplicationStartup(this.applicationStartup);
// 预处理上下文,可能包括添加BeanPostProcessor、设置Environment、初始化监听器等
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文,完成Bean的定义、初始化、依赖注入、生命周期回调等操作
refreshContext(context);
// 应用上下文刷新后执行的操作,如触发CommandLineRunner、ApplicationRunner接口实现类
afterRefresh(context, applicationArguments);
// 计算启动耗时(纳秒)
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
// 如果配置了打印启动信息,则输出启动日志和启动耗时
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 触发"started"事件,通知所有监听器Spring应用启动完成,传递上下文和启动耗时
listeners.started(context, timeTakenToStartup);
// 执行所有CommandLineRunner和ApplicationRunner接口实现类的run方法
callRunners(context, applicationArguments);
}
catch (AbandonedRunException ex) {
// 直接抛出AbandonedRunException异常,不进行处理
throw ex;
}
catch (Throwable ex) {
// 处理启动失败,如清理资源、记录错误日志等
handleRunFailure(context, ex, listeners);
// 包装异常为IllegalStateException,向上抛出
throw new IllegalStateException(ex);
}
try {
// 如果应用上下文仍在运行,则触发"ready"事件,通知所有监听器Spring应用已准备就绪
if (context.isRunning()) {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
}
catch (AbandonedRunException ex) {
// 直接抛出AbandonedRunException异常,不进行处理
throw ex;
}
catch (Throwable ex) {
// 处理启动失败,如清理资源、记录错误日志等
handleRunFailure(context, ex, null);
// 包装异常为IllegalStateException,向上抛出
throw new IllegalStateException(ex);
}
// 返回已创建并刷新完成的Spring应用上下文
return context;
}
Spring Boot的启动流程解析:
1. 启动入口 : Spring Boot应用的启动始于main()
方法,其中通常包含如下代码:
Java
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
这里,SpringApplication.run()
方法是整个启动流程的起点。
2. 初始化SpringApplication : 在run()
方法内部,首先会创建一个SpringApplication
对象。这个过程中会完成一些基础配置,如设置默认的Banner、初始化异常报告器等。同时,会根据当前环境和类路径检测确定是否启用Web特性以及具体使用哪种Web容器(如Tomcat、Jetty等)。
Java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return new SpringApplication(primarySource).run(args);
}
// 在SpringApplication构造函数中:
this.bannerMode = Banner.Mode.CONSOLE;
this.mainApplicationClass = deduceMainApplicationClass();
this.resourceLoader = new DefaultResourceLoader(getClassLoader());
this.initializers.add(new LoggingApplicationListener());
// ... 其他初始化操作
3. 加载外部配置 : 接下来,Spring Boot会加载应用的外部配置。这包括从命令行参数、系统属性、环境变量、application.properties
或application.yml
文件,以及特定于环境的 profiles 文件中获取配置信息。这些配置会被合并到一个ConfigurableEnvironment
对象中,它是Spring Environment接口的实现,用于后续提供配置属性给应用上下文。
Java
// 在prepareEnvironment()方法中:
ConfigurableEnvironment environment = getOrCreateEnvironment();
configurePropertySources(environment, args);
configureProfiles(environment, args);
// ... 其他配置操作
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType);
if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType);
}
return (environment != null) ? environment : new ApplicationEnvironment();
}
4. 创建SpringApplicationRunListeners : Spring Boot会创建一组SpringApplicationRunListener
对象,它们负责监听并响应启动过程中的各个事件,如开始启动、环境准备完毕、上下文创建、刷新、失败等。这些监听器可以是内置的,也可以由用户自定义并通过SPI机制注册。
Java
private SpringApplicationRunListeners getRunListeners(SpringApplicationArguments args) {
Class<?>[] listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,
new Class<?>[] { SpringApplication.class, this.mainApplicationClass }, args);
return new SpringApplicationRunListeners(logger, listeners);
}
// 使用SpringFactoriesLoader加载监听器:
static <T> T[] getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 从META-INF/spring.factories加载对应类型的实现类列表
}
5. 准备环境 : 在此阶段,Spring Boot会触发SpringApplicationRunListeners
的starting()
事件,并打印启动Banner(如果未禁用)。然后,它会准备应用环境,包括设置JVM的java.awt.headless
属性(如果适用),以及可能的其他环境特定配置。
Java
protected void prepareEnvironment(ConfigurableEnvironment environment, SpringApplicationArguments args) {
configureIgnoreBeanInfo(environment);
configurePropertySources(environment, args);
configureConversionService(environment);
configureResourceLoader(environment);
configureValidatorFactory(environment);
configureMessageSource(environment);
configureApplicationEventMulticaster(environment);
configureDefaultCharset(environment);
if (!this.isWebApplication()) {
configureWebEnvironment(environment, null);
}
// 触发SpringApplicationRunListeners的starting()事件
this.listeners.starting(environment);
}
6. 创建ApplicationContext : 基于应用类型(如常规Spring应用、Web应用、Reactive Web应用等)、用户配置和自动检测结果,Spring Boot会选择合适的ApplicationContext
子类(如AnnotationConfigApplicationContext
或ReactiveWebApplicationContext
)来创建应用上下文,即IoC容器。
Java
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);
}
}
// ...
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
7. 扫描和加载Bean定义 : Spring Boot会扫描指定的包(默认为主程序类所在的包及其子包)以查找@Component、@Configuration等注解标记的类,并将它们作为Bean定义加载到ApplicationContext中。同时,会处理@EnableAutoConfiguration
注解,进行自动配置,根据已有的类路径依赖和环境配置动态注册所需的Bean。
- 使用
ClassPathBeanDefinitionScanner
扫描指定包下的类。 @ComponentScan
、@SpringBootApplication
等注解驱动的组件扫描。- 处理
@Configuration
类,将其转化为BeanDefinition
并加载到BeanFactory
中。 - 处理
@EnableAutoConfiguration
注解,通过SpringFactoriesLoader
加载自动配置类并注册Bean定义。
8. 刷新ApplicationContext : 最关键的一步是调用ApplicationContext
的refresh()
方法。这个方法完成了IoC容器的完整初始化,包括:
- Bean定义的注册:将所有扫描到的Bean定义注册到BeanFactory中。
- Bean依赖解析与BeanPostProcessor注册:解析Bean之间的依赖关系,注册BeanPostProcessor和其他Bean生命周期回调接口的实现。
- Bean的实例化与初始化 :按照依赖关系顺序实例化单例Bean,并调用其初始化方法(如
@PostConstruct
注解的方法)。 - 生命周期回调 :触发
ApplicationContextAware
、BeanFactoryAware
等接口方法的调用,以及SmartInitializingSingleton
等生命周期回调。
Java
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
9. 启动完成后的工作 : 当ApplicationContext刷新完毕,Spring Boot会触发SpringApplicationRunListeners
的started()
事件,表示应用已成功启动。随后,如果有配置CommandLineRunner
或ApplicationRunner
接口的实现类,它们会被依次调用来执行自定义的启动后任务。
Java
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 按照Bean定义顺序执行runners
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
invokeRunners(runner, args);
}
}
10. 就绪通知(可选) : 如果Spring Boot应用支持"应用就绪"(Application Ready)信号,那么在所有必要的初始化工作完成且应用已经可以接受请求之后,会触发SpringApplicationRunListeners
的ready()
事件。这对于监控系统或服务发现组件来说非常重要,它们可以据此判断应用是否已经完全启动并准备好对外提供服务。
Java
private void triggerAfterStart(ApplicationArguments args) {
2 // 触发SpringApplicationRunListeners的started()事件
3 this.listeners.started(context);
4 // 如果有ApplicationReadyEvent发布者,则发布ApplicationReadyEvent
5 publishEvent(new ApplicationReadyEvent(this, args));
6}
综上所述,Spring Boot启动流程涵盖了从入口点到完全运行状态的全过程,包括初始化环境、加载配置、创建和刷新ApplicationContext、执行启动后任务以及发出就绪通知等关键步骤,确保了应用的平稳启动和高效运行。