1. Spring Boot启动流程全景透视
当我们在IDE中点击运行按钮或通过命令行执行java -jar时,Spring Boot应用就像一台精密的机器开始运转。这个看似简单的启动过程背后,隐藏着一系列精心设计的机制。让我们先从一个典型的Spring Boot启动类开始:
java复制@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
这段代码就像启动火箭的发射按钮,按下后会触发连锁反应。SpringApplication.run()方法实际上是整个启动流程的入口点,它会创建一个SpringApplication实例并调用其run方法。
关键点:Spring Boot的启动过程本质上是Spring容器的初始化过程,但相比传统Spring应用,它通过自动配置和约定优于配置的原则大幅简化了配置工作。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 启动流程深度解析
2.1 初始化阶段:SpringApplication实例化
当SpringApplication.run()被调用时,首先会执行静态run方法,该方法内部会先创建一个SpringApplication实例:
java复制public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return new SpringApplication(primarySource).run(args);
}
实例化过程中有几个关键步骤:
- 推断Web应用类型:通过检查类路径判断是Servlet Web应用(Spring MVC)、Reactive Web应用(WebFlux)还是非Web应用
- 加载应用上下文初始化器(ApplicationContextInitializer):这些组件可以在ConfigurableApplicationContext刷新前对其进行配置
- 加载应用监听器(ApplicationListener):用于监听各种应用事件
- 推断主配置类:通常就是包含main方法的类
java复制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();
}
2.2 运行阶段:SpringApplication.run()
run方法是整个启动过程的核心,它返回一个ConfigurableApplicationContext实例。让我们分解它的执行流程:
java复制public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
2.2.1 环境准备阶段
- 创建并配置环境:
prepareEnvironment()方法会创建并配置应用运行环境(Environment),这包括:- 解析命令行参数(
--server.port=8080等) - 激活指定的profile(通过
spring.profiles.active) - 加载配置文件(application.properties/yml)
- 处理随机属性值(
${random.int}等)
- 解析命令行参数(
java复制private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
- 打印Banner:从classpath下的banner.txt文件或通过Banner接口实现类加载并打印启动banner
2.2.2 应用上下文创建与准备
- 创建应用上下文:根据应用类型创建对应的ApplicationContext
- Web应用:AnnotationConfigServletWebServerApplicationContext
- Reactive Web应用:AnnotationConfigReactiveWebServerApplicationContext
- 非Web应用:AnnotationConfigApplicationContext
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);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(...);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
- 准备上下文:
prepareContext()方法会:- 设置环境
- 后置处理bean定义
- 添加BeanNameGenerator
- 注册主配置类(即包含@SpringBootApplication注解的类)
- 调用所有ApplicationContextInitializer
java复制private void prepareContext(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);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
2.2.3 容器刷新阶段
refreshContext()是整个启动过程中最复杂也最核心的部分,它调用了AbstractApplicationContext的refresh()方法:
java复制public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 准备刷新上下文
prepareRefresh();
// 获取新的BeanFactory,并加载Bean定义
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备BeanFactory使用
prepareBeanFactory(beanFactory);
try {
// 允许子类对BeanFactory进行后处理
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 调用BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化消息源
initMessageSource();
// 初始化事件广播器
initApplicationEventMulticaster();
// 初始化特定上下文子类中的特殊bean
onRefresh();
// 检查并注册监听器
registerListeners();
// 实例化所有剩余的非懒加载单例bean
finishBeanFactoryInitialization(beanFactory);
// 完成刷新过程,发布相应事件
finishRefresh();
}
catch (BeansException ex) {
// 处理异常...
}
finally {
// 重置公共缓存...
resetCommonCaches();
contextRefresh.end();
}
}
}
其中几个关键子过程:
- BeanFactory准备:创建并配置DefaultListableBeanFactory
- Bean定义加载:通过BeanDefinitionReader读取配置类上的注解(@ComponentScan, @Import等)并注册Bean定义
- BeanFactory后处理:执行所有BeanFactoryPostProcessor(包括处理@Configuration类的ConfigurationClassPostProcessor)
- BeanPostProcessor注册:注册所有BeanPostProcessor,这些处理器会在bean初始化前后执行
- 消息源和事件广播器初始化:为国际化消息和事件发布做准备
- onRefresh():模板方法,子类可以实现特定逻辑。在Web应用中,这会创建并启动内嵌的Web服务器
- 单例bean初始化:实例化所有非懒加载的单例bean
2.2.4 自动配置实现机制
Spring Boot的自动配置是通过@EnableAutoConfiguration注解和spring.factories文件实现的。让我们看看具体实现:
- @SpringBootApplication注解:这是一个复合注解,包含@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan
java复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// ...
}
- @EnableAutoConfiguration:通过@Import导入AutoConfigurationImportSelector
java复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// ...
}
- AutoConfigurationImportSelector:从META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中加载自动配置类
java复制public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// ...
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
.getCandidates();
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
// ...
}
- 条件化配置:自动配置类通常使用@Conditional系列注解(如@ConditionalOnClass, @ConditionalOnMissingBean等)确保只在特定条件下生效
java复制@AutoConfiguration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@ImportRuntimeHints(WebMvcRuntimeHints.class)
public class WebMvcAutoConfiguration {
// ...
}
3. 内嵌Web服务器启动过程
对于Web应用,Spring Boot会自动配置并启动一个内嵌的Web服务器(Tomcat、Jetty或Undertow)。这个过程主要发生在AbstractApplicationContext的onRefresh()方法中:
java复制protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
具体实现细节:
- ServletWebServerApplicationContext:负责Web服务器的创建和管理
java复制public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
// ...
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
// ...
}
- 自动配置:通过WebServerFactoryCustomizer和ServletContextInitializer对Web服务器进行定制
java复制@AutoConfiguration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public class TomcatServletWebServerFactoryAutoConfiguration {
@Bean
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(environment, serverProperties);
}
@Bean
@ConditionalOnMissingBean
public DefaultServletWebServerFactoryCustomizer defaultServletWebServerFactoryCustomizer(
ServerProperties serverProperties, WebMvcProperties webMvcProperties,
ObjectProvider<DispatcherServletPath> dispatcherServletPath) {
return new DefaultServletWebServerFactoryCustomizer(serverProperties,
webMvcProperties, dispatcherServletPath);
}
// ...
}
- 端口绑定:通过ServerProperties配置服务器端口和其他属性
java复制@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private InetAddress address;
private final Servlet servlet = new Servlet();
private final Tomcat tomcat = new Tomcat();
private final Jetty jetty = new Jetty();
private final Undertow undertow = new Undertow();
private final Netty netty = new Netty();
// ...
}
4. 启动后处理
容器刷新完成后,Spring Boot还会执行一些后续操作:
- 调用ApplicationRunner和CommandLineRunner:这些接口的实现类会在应用启动后执行
java复制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());
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);
}
}
}
-
发布ApplicationReadyEvent:通知所有监听器应用已准备就绪
-
启动信息统计:记录并打印启动耗时等信息
5. 常见问题与调试技巧
5.1 启动失败排查
当Spring Boot应用启动失败时,可以按照以下步骤排查:
- 检查异常堆栈:Spring Boot通常会提供详细的错误信息
- 启用调试日志:添加
--debug参数或设置logging.level.root=DEBUG - 检查自动配置报告:通过
debug=true查看哪些自动配置类被应用/排除 - 检查Bean定义冲突:使用
spring.main.allow-bean-definition-overriding=true临时解决
5.2 性能优化建议
- 延迟初始化:设置
spring.main.lazy-initialization=true可以加快启动速度,但可能导致首次请求延迟 - 排除不必要的自动配置:使用
@SpringBootApplication(exclude={...})排除不需要的自动配置类 - 使用组件扫描过滤:通过
@ComponentScan的excludeFilters属性减少扫描范围 - JVM参数调优:适当增加初始堆内存(-Xms)可以减少GC次数
5.3 自定义启动逻辑
- 自定义Banner:在resources目录下添加banner.txt文件或实现Banner接口
- 添加ApplicationContextInitializer:通过META-INF/spring.factories或SpringApplication.addInitializers()注册
- 自定义EnvironmentPostProcessor:在应用环境准备好后对其进行修改
- 实现SpringApplicationRunListener:监听启动过程中的各个阶段
java复制public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// 应用启动后执行的逻辑
}
}
6. 启动流程可视化
为了更直观地理解整个启动过程,我们可以将其分为几个主要阶段:
-
初始化阶段:
- 创建SpringApplication实例
- 推断Web应用类型
- 加载ApplicationContextInitializer和ApplicationListener
-
环境准备阶段:
- 创建并配置Environment
- 处理命令行参数
- 加载配置文件
- 打印Banner
-
上下文创建阶段:
- 创建ApplicationContext
- 准备上下文(设置环境、注册单例bean等)
- 加载主配置类
-
容器刷新阶段:
- 加载Bean定义
- 执行BeanFactoryPostProcessor
- 注册BeanPostProcessor
- 初始化消息源和事件广播器
- 初始化单例bean
- 启动内嵌Web服务器(Web应用)
-
启动后处理阶段:
- 调用ApplicationRunner和CommandLineRunner
- 发布ApplicationReadyEvent
理解Spring Boot的启动流程对于诊断启动问题、进行性能优化以及实现高级定制功能都非常重要。通过深入源码分析,我们不仅能够更好地使用Spring Boot,还能在遇到问题时快速定位和解决。
