
当我们在2025年开发Spring Boot应用时,理解其启动流程仍然是构建高性能应用的基础。Spring Boot的启动过程是一个精心设计的链条,每个环节都承担着特定的职责,共同构建起完整的应用上下文。
启动流程始于SpringApplication类的实例化。在main方法中调用SpringApplication.run()时,实际上会先创建一个SpringApplication实例。这个初始化过程会执行几个关键操作:
在2025年的Spring Boot 3.x版本中,这个初始化过程仍然保持稳定,但内部实现细节有所优化,比如对GraalVM原生镜像支持的改进。
SpringApplication.run()方法是整个启动过程的核心入口,它按照特定顺序执行多个关键步骤:
在刷新上下文阶段,Spring Boot会执行一系列关键操作:
配置类解析:@SpringBootApplication注解标注的启动类会被作为主要配置源。这个复合注解包含:
自动配置加载:Spring Boot通过spring-boot-autoconfigure模块提供的自动配置类,根据类路径和现有bean定义条件化地加载各种功能模块。在2025年的版本中,自动配置机制变得更加智能,能够根据运行时指标动态调整配置。
Bean定义注册:这个阶段会处理所有通过@Component及其派生注解(@Service、@Controller等)标记的类,将它们转换为BeanDefinition并注册到容器中。
Bean实例化:在refresh()方法的最后阶段,Spring会实例化所有非懒加载的单例bean。这个过程包括:
理解Spring Boot的启动流程不仅要知道标准流程,还需要了解关键的扩展点:
在2025年的Spring生态中,这些扩展点被广泛应用于各种企业级特性,如多租户支持、动态配置切换等场景。
在Spring Boot应用的启动过程中,Environment扮演着至关重要的角色,它是整个应用配置体系的核心容器。当我们深入分析SpringApplication.run()方法的执行过程时,会发现StandardServletEnvironment的创建是整个启动流程中最早完成的关键步骤之一。

当Spring Boot应用启动时,在SpringApplication的构造方法中会通过deduceWebApplicationType()方法推断应用类型。对于典型的Web应用(Servlet环境),Spring Boot会创建一个StandardServletEnvironment实例。这个创建过程发生在SpringApplication的prepareEnvironment()方法被调用之前,是环境准备阶段的第一步。
具体来说,在SpringApplication.run()方法的执行流程中:
StandardServletEnvironment的类继承关系值得关注:
这种设计使得StandardServletEnvironment既具备了标准环境的基本功能,又扩展了对Servlet环境特有的支持。在AbstractEnvironment中,实现了Environment接口定义的大部分方法,包括属性访问、Profile管理等核心功能。
StandardServletEnvironment的初始化主要经历以下几个关键步骤:
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}从这段代码可以看出,StandardServletEnvironment会按顺序添加以下属性源:
StandardServletEnvironment针对Web环境做了特殊处理,主要体现在:
这些特性使得Spring Boot应用能够无缝集成到Servlet容器中,并能够访问容器提供的各种配置参数。
StandardServletEnvironment还负责处理Spring Profile的激活状态。在初始化过程中,它会:
Profile的处理对于后续的Bean定义加载和条件化配置至关重要,它决定了哪些配置类和Bean定义会被实际注册到容器中。
在传统的Spring MVC应用中,StandardServletEnvironment与DispatcherServlet紧密配合:
这种设计保证了整个应用中的配置一致性,避免了不同层级上下文环境不一致导致的问题。
在2025年的Spring Boot 3.x版本中,StandardServletEnvironment的行为有了以下优化:
这些改进使得现代Spring Boot应用在复杂的企业环境中能够更快启动,同时保持配置系统的灵活性。
理解StandardServletEnvironment的创建和初始化过程,对于深入掌握Spring Boot的配置系统至关重要。它不仅决定了配置参数的加载顺序,还影响着整个应用的启动行为和运行时特性。
在Spring Boot启动过程中,PropertySources的加载顺序直接决定了配置属性的优先级,这是理解Spring Boot配置机制的核心要点。让我们深入剖析这个关键流程。

Spring Boot通过PropertySources接口管理所有配置源,其实现类MutablePropertySources内部维护着一个PropertySource的有序列表。这个顺序决定了当存在同名属性时,哪个配置源的值会被最终采用。
在StandardServletEnvironment初始化时,会按以下顺序建立PropertySources链:
第一阶段:基础环境准备
当SpringApplication启动时,首先会创建ConfigurableEnvironment实例。对于Web应用,默认使用StandardServletEnvironment。其构造函数会调用customizePropertySources()方法初始化基础PropertySources:
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}第二阶段:动态配置加载
在prepareEnvironment()阶段,Spring Boot会动态添加更多PropertySource:
实际加载顺序(从高到低):
场景1:命令行参数覆盖
当我们在启动命令中指定:
java -jar app.jar --server.port=8081这个值会覆盖application.properties中定义的server.port,因为命令行参数的优先级最高。
场景2:Profile特定配置
假设有以下文件:
实际会采用8082端口,因为激活的是dev profile。
场景3:系统属性与环境变量
在Linux系统中设置:
export SERVER_PORT=8084这会覆盖application.properties中的配置,因为环境变量的优先级高于配置文件。
在ConfigFileApplicationListener中可以看到配置文件加载的具体实现:
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}这个方法会按照以下顺序搜索配置文件:
误区1:application.yml总是优先于application.properties 实际上,当两者同时存在时,Spring Boot会合并两者的配置,而不是简单的覆盖。相同属性的优先级取决于它们在文件中的位置顺序。
误区2:@PropertySource优先级高于系统环境变量 实际上@PropertySource添加的属性源优先级低于系统环境变量,除非显式调整PropertySources顺序。
误区3:所有配置都可以通过环境变量覆盖 只有符合Spring Boot宽松绑定规则的属性才能通过环境变量覆盖,例如server.port对应SERVER_PORT。
在Spring Boot启动过程中,prepareEnvironment()方法扮演着环境准备的关键角色。这个方法位于SpringApplication.run()的执行流程中,负责创建和配置应用运行所需的环境对象,并加载各类配置源。让我们深入剖析这个方法的实现细节和背后的设计思想。
当SpringApplication启动时,prepareEnvironment()方法首先会通过getOrCreateEnvironment()获取或创建Environment实例。在Web环境中,默认会创建StandardServletEnvironment对象。这个环境对象继承自StandardEnvironment,专门为Servlet容器设计。
protected ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 创建或获取Environment对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境(包括PropertySources和Profiles)
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 发布环境准备事件
listeners.environmentPrepared(bootstrapContext, environment);
// 将环境绑定到SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
return environment;
}configureEnvironment()方法会按照特定顺序加载各种配置源,形成PropertySources的层级结构:
configurePropertySources()方法,Spring Boot会首先处理命令行传入的参数。这些参数具有最高优先级,可以覆盖其他配置源中的同名属性。
System.getProperties()获取,包括通过-D参数设置的JVM系统属性。
System.getenv()获取,在容器化部署场景中尤为重要。
application.properties和application.yml文件,Spring Boot会按照以下顺序查找并加载:
ConfigFileApplicationListener负责处理配置文件的加载工作。它会根据spring.config.name(默认为"application")和spring.config.location属性确定配置文件的名称和位置。
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}Spring Boot采用"后来者优先"的原则处理属性冲突,具体优先级从高到低如下:
在环境准备完成后,Spring Boot会通过bindToSpringApplication()方法将环境对象绑定到SpringApplication实例。这个步骤会处理spring.main前缀的属性,允许通过外部配置来覆盖SpringApplication的默认设置,如:
spring.main.banner-mode=off
spring.main.lazy-initialization=true在实际开发中,了解环境准备过程对调试配置问题非常有帮助。可以通过以下方式查看最终的PropertySources:
--debug参数@Autowired
private Environment env;
@PostConstruct
public void printProperties() {
System.out.println("All property sources:");
((AbstractEnvironment) env).getPropertySources().forEach(ps ->
System.out.println(ps.getName()));
}在Spring Boot面试中,关于配置加载机制的问题几乎必考。以下是2025年面试中最常见的几个问题及其深度解析,帮助开发者掌握Spring Boot配置系统的核心原理。
Spring Boot采用了一种精心设计的PropertySource加载顺序机制,确保配置能够按照预期被覆盖和继承。完整的加载顺序如下(从高优先级到低优先级):
--传递的参数,如java -jar app.jar --server.port=8081java:comp/env的配置System.getProperties()获取random.*相关的属性/config子目录下的application-{profile}.properties/ymlapplication-{profile}.properties/yml/config包中的application-{profile}.properties/ymlapplication-{profile}.properties/ymlapplication.properties/yml(不带profile)这个顺序体现了Spring Boot的重要设计哲学:特定环境配置优先于通用配置,外部配置优先于内部配置。例如,生产环境部署时,运维人员可以通过外部配置文件覆盖开发人员定义的默认值。
命令行参数之所以能覆盖其他配置,是因为它被添加到了最高优先级的CommandLinePropertySource中。其实现原理如下:
SpringApplication.run()方法中,会调用DefaultApplicationArguments解析命令行参数SimpleCommandLinePropertySource或JOptCommandLinePropertySourceSpringApplication.prepareEnvironment()方法,这个PropertySource被添加到Environment的最前面覆盖行为的典型示例:
# 命令行参数将覆盖application.properties中的server.port配置
java -jar demo.jar --server.port=9090值得注意的是,Spring Boot 3.x之后对命令行参数处理做了优化:
--name=value和--name valueSpring Profiles是处理多环境配置的核心机制,其工作原理包含以下关键点:
spring.profiles.active指定激活的profileapplication.propertiesapplication-{profile}.propertiesapplication-test.properties2025年最新实践中,推荐使用以下多环境配置方案:
# application.yml
spring:
profiles:
active: @activatedProperties@ # Maven/Gradle过滤
---
# 开发环境配置
spring:
profiles: dev
server:
port: 8080
---
# 生产环境配置
spring:
profiles: prod
server:
port: 80在高级应用场景中,可能需要实现自定义的PropertySource。标准做法是:
PropertySource接口或继承EnumerablePropertySourceEnvironmentPostProcessor接口spring.factories中注册处理器@PropertySource注解一个典型的Redis配置源实现示例:
public class RedisPropertySource extends EnumerablePropertySource<RedisTemplate> {
public RedisPropertySource(String name, RedisTemplate source) {
super(name, source);
}
@Override
public String[] getPropertyNames() {
return source.keys("*").toArray(new String[0]);
}
@Override
public Object getProperty(String name) {
return source.opsForValue().get(name);
}
}注册自定义PropertySource的最佳时机是在ApplicationContextInitializer中,确保它在其他标准PropertySource之前被加载。
当配置加载出现问题时,Spring Boot会按照以下策略处理:
ConversionFailedExceptionBindValidationException@Value(required=true)或@ConfigurationProperties的校验注解时会报错2025年版本中新增的改进包括:
对于使用Spring Cloud Config或需要运行时刷新的应用,需要注意:
@RefreshScope的工作机制:
刷新边界:
最佳实践:
@RefreshScope
@ConfigurationProperties("app.config")
public class AppConfig {
// 配置属性
}这些问题的深入理解不仅有助于面试,更能帮助开发者在实际项目中构建更健壮的配置系统。
在Spring Boot应用的开发与优化过程中,深入理解其启动流程与上下文初始化机制绝非可有可无的"高级知识",而是每一位开发者必须掌握的核心竞争力。通过前文对StandardServletEnvironment创建过程、PropertySources加载顺序以及prepareEnvironment()方法的详细剖析,我们可以清晰地看到,Spring Boot的启动过程是一个精心设计的配置体系构建过程。
理解Environment的构建机制,能帮助开发者在面对配置冲突时快速定位问题根源。例如,当发现命令行参数未能按预期覆盖配置文件时,熟悉PropertySources加载顺序的开发者会立即检查ServletConfig或ServletContext中是否存在同名属性。这种问题定位能力在2025年的微服务架构中尤为重要,因为现代应用往往需要同时处理Kubernetes环境变量、Config Server配置和本地配置文件的多层配置源。
对于配置加载优先级的掌握,直接影响着应用的部署灵活性。在当下主流的云原生部署场景中,通过命令行参数动态调整数据库连接池大小或日志级别的需求极为常见。了解Spring Boot如何通过CommandLinePropertySource实现参数覆盖,开发者就能在Docker容器启动时优雅地注入运行时配置,而不必重新构建镜像。
值得注意的是,随着Spring 6.x版本的演进,环境初始化流程虽然保持了核心设计理念,但在实现细节上有所优化。例如,对GraalVM原生镜像的支持使得部分环境检测逻辑提前到了编译期。这就要求开发者不仅要理解原理,还要持续跟进框架的最新发展。
在性能优化方面,启动流程的理解同样关键。通过分析StandardServletEnvironment的初始化过程,我们可以识别出哪些PropertySource的加载可能成为性能瓶颈。例如,JNDI查找在网络环境不佳时可能显著延长启动时间,这时就可以考虑调整加载顺序或延迟初始化策略。
对于面试中常见的"配置优先级"问题,表面看是考察记忆,实则是检验候选人对Spring Boot设计哲学的理解程度。优秀的开发者应该能够从Environment体系的设计出发,解释为何命令行参数需要优先于系统属性,以及这种设计如何支持十二要素应用原则中的"配置"准则。
在微服务监控领域,这些知识同样具有现实意义。当需要构建自定义的健康检查指标时,准确理解Environment的初始化时机和生命周期,才能确保监控组件正确获取到所有必要的配置属性。
[1] : https://spring.io/
[2] : https://www.cainiaojc.com/spring/
[3] : https://springdoc.cn/docs/
[4] : https://springframework.org.cn/