⚙️配置文件加载顺序与优先级规则
文章目录
🌐 一、配置加载体系总览:Environment 与 PropertySource
🏗️ Spring Boot 配置架构核心组件
配置体系的核心接口关系:
🔧 Environment 实现类体系
标准环境实现类结构:
// Spring Boot 中常用的环境实现
public class StandardEnvironment extends AbstractEnvironment {
// 系统属性源
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
// 系统环境变量源
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
// 1. 添加系统属性源
propertySources.addLast(
new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,
getSystemProperties()));
// 2. 添加系统环境变量源
propertySources.addLast(
new MapPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
getSystemEnvironment()));
}
}
// Web 应用环境扩展
public class StandardServletEnvironment extends StandardEnvironment {
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
// 先调用父类方法
super.customizePropertySources(propertySources);
// 添加Servlet相关属性源
propertySources.addLast(
new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(
new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
}
}
📁 二、application.yml 与 bootstrap.yml 的区别
🎯 两种配置文件的定位差异
bootstrap.yml 的特殊地位:
# bootstrap.yml - 用于Spring Cloud上下文引导
spring:
application:
name: my-application # 应用名称(用于服务发现)
cloud:
config:
uri: http://config-server:8888 # 配置中心地址
fail-fast: true # 快速失败
retry:
initial-interval: 2000 # 重试间隔
max-attempts: 10 # 最大重试次数
profiles:
active: dev # 激活的Profile
application.yml 的应用配置:
# application.yml - 应用级别配置
server:
port: 8080 # 服务器端口
servlet:
context-path: /api # 上下文路径
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: app_user
password: ${DB_PASSWORD:default} # 环境变量优先
management:
endpoints:
web:
exposure:
include: health,info,metrics # 监控端点
🔄 加载时机与顺序对比
配置文件加载时序图:
关键差异总结表:
| 特性 | bootstrap.yml | application.yml |
|---|---|---|
| 加载时机 | 应用上下文创建之前加载 | 应用上下文创建期间加载 |
| 所属上下文 | BootstrapContext(引导上下文) | ApplicationContext(应用上下文) |
| 主要用途 | 外部配置中心连接、系统级配置、加密配置 | 应用业务配置、本地环境配置 |
| 优先级 | 较低(可被 application.yml 覆盖) | 较高(可覆盖 bootstrap.yml) |
| Spring Cloud 角色 | 必需(如 Nacos、Config Server) | 可选 |
| 普通 Spring Boot 项目 | 可选(很少使用) | 必需 |
| 典型内容 | spring.cloud.config.uri、spring.application.name、加密密钥等 | server.port、spring.datasource、业务配置等 |
🔄 三、配置文件加载顺序详解
🏆 配置源优先级完整排行榜
Spring Boot 配置源加载顺序:
graph TB
A[配置源] --> B[优先级顺序]
B --> C1[1. 命令行参数]
B --> C2[2. SPRING_APPLICATION_JSON]
B --> C3[3. ServletConfig初始化参数]
B --> C4[4. ServletContext初始化参数]
B --> C5[5. JNDI属性]
B --> C6[6. Java系统属性]
B --> C7[7. 操作系统环境变量]
B --> C8[8. RandomValuePropertySource]
B --> C9[9. Profile特定应用配置]
B --> C10[10. 应用配置]
B --> C11[11. Profile特定bootstrap配置]
B --> C12[12. Bootstrap配置]
B --> C13[13. @PropertySource注解]
B --> C14[14. 默认属性]
style C1 fill:#bbdefb,stroke:#333,stroke-width:2px
style C14 fill:#ffccbc,stroke:#333
📊 详细配置源解析
配置源加载源码分析:
public class SpringApplication {
/**
* 准备环境的核心方法
*/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 1. 创建或获取环境实例
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2. 配置环境(处理命令行参数等)
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 3. 触发环境准备事件
listeners.environmentPrepared(environment);
// 4. 绑定到SpringApplication
bindToSpringApplication(environment);
// 5. 环境转换(如果需要)
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 6. 附加配置属性源
ConfigurationPropertySources.attach(environment);
return environment;
}
}
🔧 配置属性源附加过程
ConfigurationPropertySources.attach() 源码:
public abstract class ConfigurationPropertySources {
public static void attach(Environment environment) {
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
// 检查是否已附加
if (sources.get(ATTACHED_PROPERTY_SOURCE_NAME) != null) {
return;
}
// 创建ConfigurationPropertySourcesPropertySource
ConfigurationPropertySourcesPropertySource attached = new ConfigurationPropertySourcesPropertySource(
ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources));
// 插入到属性源列表的最前面
sources.addFirst(attached);
}
}
📝 配置文件搜索路径规则
Spring Boot 配置文件搜索顺序:
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, Ordered {
// 默认搜索路径
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
// 默认配置文件名称
private static final String DEFAULT_NAMES = "application";
/**
* 获取所有可能的配置文件位置
*/
private Set<String> getSearchLocations() {
Set<String> locations = new LinkedHashSet<>();
// 1. 用户自定义位置
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
for (String path : asResolvedSet(this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) {
if (!path.contains("$") && !path.startsWith("classpath:")) {
path = "file:" + path;
}
locations.add(path);
}
}
// 2. 添加默认搜索位置
locations.addAll(asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
return locations;
}
/**
* 获取所有可能的配置文件名称
*/
private Set<String> getSearchNames() {
Set<String> names = new LinkedHashSet<>();
// 1. 用户自定义名称
if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
names.addAll(asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY), null));
}
// 2. 添加默认名称
names.addAll(asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES));
return names;
}
}
🔍 四、ConfigFileApplicationListener 源码解析
🎯 环境后处理器核心逻辑
ConfigFileApplicationListener 类结构:
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, Ordered {
// 配置位置属性键
public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
// 配置名称属性键
public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
// 附加位置属性键
public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 1. 添加属性源占位符
addPropertySources(environment, application);
// 2. 绑定配置数据
bindToSpringApplication(environment, application);
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 获取随机值属性源(用于随机端口等)
RandomValuePropertySource.addToEnvironment(environment);
// 加载应用配置文件
Loader loader = new Loader(environment, resourceLoader);
loader.load();
}
}
🔧 配置文件加载器实现
Loader 内部类源码分析:
private class Loader {
private final ConfigurableEnvironment environment;
private final PropertySourceLoader[] loaders;
void load() {
// 1. 获取激活的Profile
FilteredProfiles filteredProfiles = getProfiles();
// 2. 加载默认配置文件(无Profile)
Loader.LoadedPropertySources loaded = load("application", filteredProfiles, null);
// 3. 加载Profile特定配置文件
for (String profile : filteredProfiles.getActive()) {
Loader.LoadedPropertySources loadedForProfile = load("application", filteredProfiles, profile);
loaded.addAll(loadedForProfile);
}
// 4. 处理导入的配置
processImports(loaded);
}
private Loader.LoadedPropertySources load(String name, FilteredProfiles filteredProfiles, String profile) {
Loader.LoadedPropertySources loaded = new Loader.LoadedPropertySources();
// 在所有搜索位置查找配置文件
for (String location : getSearchLocations()) {
// 检查是否存在配置文件
if (!location.endsWith("/")) {
location = location + "/";
}
for (PropertySourceLoader loader : this.loaders) {
// 尝试加载不同格式的配置文件
for (String extension : loader.getFileExtensions()) {
Loader.LoadedPropertySources loadedFromLocation = load(
loader, location + name, profile, extension, filteredProfiles);
loaded.addAll(loadedFromLocation);
}
}
}
return loaded;
}
}
📊 属性源加载器机制
PropertySourceLoader 接口实现:
public interface PropertySourceLoader {
/**
* 支持的文件扩展名
*/
String[] getFileExtensions();
/**
* 加载属性源
*/
PropertySource<?> load(String name, Resource resource, String profile) throws IOException;
}
// YAML属性源加载器实现
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
// 解析YAML文件
List<Document> documents = YamlDocumentLoader.load(resource, profile);
if (documents.isEmpty()) {
return null;
}
// 创建属性源
return new OriginTrackedMapPropertySource(name, getFlattenedMap(documents));
}
}
// Properties属性源加载器实现
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
@Override
public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
// 解析Properties文件
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
if (properties.isEmpty()) {
return null;
}
return new PropertiesPropertySource(name, properties);
}
}
💻 五、实战:多环境配置与优先级验证
🎯 多环境配置最佳实践
完整的配置文件结构:
src/main/resources/
├── application.yml # 主配置文件(默认配置)
├── application-dev.yml # 开发环境配置
├── application-test.yml # 测试环境配置
├── application-prod.yml # 生产环境配置
└── bootstrap.yml # 引导配置(可选)
主配置文件示例:
# application.yml - 默认配置
spring:
profiles:
active: dev # 默认激活开发环境
server:
port: 8080
servlet:
context-path: /api
logging:
level:
root: INFO
com.example: DEBUG
# 公共数据源配置(可被环境特定配置覆盖)
database:
pool-size: 10
timeout: 30000
环境特定配置示例:
# application-dev.yml - 开发环境
spring:
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
driver-class-name: org.h2.Driver
logging:
level:
com.example: TRACE
org.hibernate.SQL: DEBUG
debug: true # 启用调试模式
---
# application-test.yml - 测试环境
spring:
datasource:
url: jdbc:mysql://test-db:3306/app_test
username: tester
password: test123
driver-class-name: com.mysql.cj.jdbc.Driver
management:
endpoints:
web:
exposure:
include: health,info,metrics
---
# application-prod.yml - 生产环境
spring:
datasource:
url: jdbc:mysql://prod-db-cluster:3306/app_prod
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 20
connection-timeout: 10000
management:
endpoints:
web:
exposure:
include: health # 生产环境只暴露健康检查
logging:
level:
root: WARN
com.example: INFO
🔧 配置优先级验证工具
配置源调试工具类:
@Component
@Slf4j
public class ConfigurationDebugger implements ApplicationRunner {
@Autowired
private ConfigurableEnvironment environment;
@Override
public void run(ApplicationArguments args) throws Exception {
logConfigurationSources();
testPropertyPrecedence();
}
/**
* 打印所有配置源及其优先级
*/
private void logConfigurationSources() {
log.info("=== 配置源优先级报告 ===");
MutablePropertySources propertySources = environment.getPropertySources();
int index = 1;
for (PropertySource<?> propertySource : propertySources) {
log.info("{}. {} [{}]", index++, propertySource.getName(),
propertySource.getClass().getSimpleName());
// 显示前几个属性示例
if (propertySource instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) propertySource;
String[] propertyNames = enumerable.getPropertyNames();
int sampleSize = Math.min(propertyNames.length, 3);
for (int i = 0; i < sampleSize; i++) {
String name = propertyNames[i];
Object value = propertySource.getProperty(name);
log.info(" {} = {}", name, value);
}
if (propertyNames.length > sampleSize) {
log.info(" ... 还有 {} 个属性", propertyNames.length - sampleSize);
}
}
log.info(" ---");
}
}
/**
* 测试属性优先级
*/
private void testPropertyPrecedence() {
log.info("=== 属性优先级测试 ===");
String[] testProperties = {
"server.port",
"spring.datasource.url",
"logging.level.root",
"management.endpoints.web.exposure.include"
};
for (String property : testProperties) {
String value = environment.getProperty(property);
String source = findPropertySource(property);
log.info("{} = {} [来源: {}]", property, value, source);
}
}
/**
* 查找属性来源
*/
private String findPropertySource(String propertyName) {
MutablePropertySources propertySources = environment.getPropertySources();
for (PropertySource<?> propertySource : propertySources) {
if (propertySource.containsProperty(propertyName)) {
return propertySource.getName();
}
}
return "未找到";
}
}
📊 配置覆盖验证测试
多环境配置覆盖测试:
@SpringBootTest
@ActiveProfiles({"dev", "test"}) // 激活多个Profile测试覆盖规则
class ConfigurationPrecedenceTest {
@Autowired
private ConfigurableEnvironment environment;
@Test
void testProfilePrecedence() {
// 测试Profile配置覆盖规则
assertThat(environment.getProperty("spring.datasource.url"))
.isEqualTo("jdbc:mysql://test-db:3306/app_test"); // test覆盖dev
assertThat(environment.getProperty("logging.level.com.example"))
.isEqualTo("TRACE"); // dev配置(test中没有此配置)
}
@Test
void testCommandLineArgumentsPrecedence() {
// 模拟命令行参数
SimpleCommandLinePropertySource commandLineSource =
new SimpleCommandLinePropertySource("--server.port=9090", "--debug=true");
environment.getPropertySources().addFirst(commandLineSource);
// 命令行参数应该具有最高优先级
assertThat(environment.getProperty("server.port")).isEqualTo("9090");
assertThat(environment.getProperty("debug")).isEqualTo("true");
}
@Test
void testSystemPropertiesPrecedence() {
// 设置系统属性
System.setProperty("app.custom.setting", "system-value");
// 系统属性应该覆盖配置文件
assertThat(environment.getProperty("app.custom.setting"))
.isEqualTo("system-value");
}
}
🐛 六、配置文件冲突与调试技巧
⚠️ 常见配置冲突场景
配置覆盖冲突示例:
# 冲突场景1:多Profile配置冲突
# application-common.yml
app:
feature:
enabled: true
timeout: 5000
# application-dev.yml
app:
feature:
timeout: 10000 # 覆盖common配置
# application-prod.yml
app:
feature:
enabled: false # 覆盖common配置
timeout: 30000 # 覆盖dev配置
# 冲突场景2:多文件配置冲突
# application.yml
database:
host: localhost
port: 3306
# config/application.yml(更高优先级)
database:
host: 192.168.1.100 # 覆盖classpath下的配置
🔧 配置冲突调试工具
配置冲突检测器:
@Component
@Slf4j
public class ConfigurationConflictDetector {
@Autowired
private ConfigurableEnvironment environment;
@EventListener
public void detectConflicts(ApplicationReadyEvent event) {
log.info("=== 配置冲突检测 ===");
detectDuplicateProperties();
detectProfileConflicts();
detectTypeMismatches();
}
/**
* 检测重复属性定义
*/
private void detectDuplicateProperties() {
Map<String, List<String>> propertySourcesMap = new HashMap<>();
MutablePropertySources sources = environment.getPropertySources();
// 收集所有属性及其来源
for (PropertySource<?> source : sources) {
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
for (String propertyName : enumerable.getPropertyNames()) {
propertySourcesMap.computeIfAbsent(propertyName, k -> new ArrayList<>())
.add(source.getName());
}
}
}
// 报告重复属性
propertySourcesMap.entrySet().stream()
.filter(entry -> entry.getValue().size() > 1)
.forEach(entry -> {
log.warn("🔴 属性冲突: {} 在多个配置源中定义: {}",
entry.getKey(), entry.getValue());
// 显示实际值
String value = environment.getProperty(entry.getKey());
log.info(" 当前生效值: {} = {}", entry.getKey(), value);
});
}
/**
* 检测Profile配置冲突
*/
private void detectProfileConflicts() {
String[] activeProfiles = environment.getActiveProfiles();
if (activeProfiles.length > 1) {
log.info("激活的Profile: {}", Arrays.toString(activeProfiles));
log.warn("多个Profile激活,注意配置覆盖规则:后面的Profile会覆盖前面的");
}
}
}
📝 配置调试最佳实践
Spring Boot 配置调试配置:
# 开启详细配置日志
logging.level.org.springframework.boot.env=DEBUG
logging.level.org.springframework.core.env=TRACE
# 显示配置加载详情
debug=true
自定义配置调试端点:
@RestController
@Endpoint(id = "config")
@Slf4j
public class ConfigurationEndpoint {
@Autowired
private ConfigurableEnvironment environment;
@ReadOperation
public Map<String, Object> getConfigurationDetails() {
Map<String, Object> details = new LinkedHashMap<>();
// 1. 配置源信息
details.put("propertySources", getPropertySourceDetails());
// 2. 激活的Profile
details.put("activeProfiles", environment.getActiveProfiles());
// 3. 默认Profile
details.put("defaultProfiles", environment.getDefaultProfiles());
// 4. 关键配置值
details.put("keyProperties", getKeyProperties());
return details;
}
private List<Map<String, Object>> getPropertySourceDetails() {
List<Map<String, Object>> sources = new ArrayList<>();
MutablePropertySources propertySources = environment.getPropertySources();
for (PropertySource<?> source : propertySources) {
Map<String, Object> sourceInfo = new HashMap<>();
sourceInfo.put("name", source.getName());
sourceInfo.put("type", source.getClass().getSimpleName());
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
sourceInfo.put("propertyCount", enumerable.getPropertyNames().length);
}
sources.add(sourceInfo);
}
return sources;
}
private Map<String, String> getKeyProperties() {
String[] keyProperties = {
"server.port",
"spring.datasource.url",
"spring.application.name",
"logging.level.root"
};
Map<String, String> values = new HashMap<>();
for (String property : keyProperties) {
values.put(property, environment.getProperty(property));
}
return values;
}
}
💎 七、总结:让配置更可控、更可预期
🎯 配置加载核心规则总结
Spring Boot 配置优先级黄金法则:
| 优先级 | 配置源 | 覆盖能力 | 典型使用场景 |
|---|---|---|---|
| 1️⃣ | 命令行参数 (--server.port=9090) | ⭐⭐⭐⭐(最强) | 临时参数、容器部署、测试调试 |
| 2️⃣ | Java 系统属性 (-Dserver.port=9090) | ⭐⭐⭐⭐ | JVM 启动参数、系统级设置 |
| 3️⃣ | 操作系统环境变量 (SERVER_PORT=9090) | ⭐⭐⭐ | Docker、K8s、CI/CD |
| 4️⃣ | Profile 特定应用配置 (application-dev.yml) | ⭐⭐ | 环境差异化配置(dev、test、prod) |
| 5️⃣ | 应用配置文件 (application.yml) | ⭐⭐ | 默认业务配置、通用属性 |
| 6️⃣ | Profile 特定 Bootstrap 配置 (bootstrap-dev.yml) | ⭐ | Spring Cloud 环境配置 |
| 7️⃣ | Bootstrap 配置 (bootstrap.yml) | ⭐(最弱) | 引导阶段配置、远程配置中心接入 |
🔧 配置管理最佳实践
多环境配置策略:
# 1. 使用Profile进行环境隔离
spring:
profiles:
active: @activatedProperties@ # Maven过滤替换
# 2. 分层配置结构
# application.yml - 基础配置
app:
version: 1.0.0
base-config: value
# application-{env}.yml - 环境特定配置
app:
env-specific: value
# application-{feature}.yml - 功能模块配置
feature:
module:
enabled: true
# 3. 安全配置分离
# bootstrap.yml - 敏感配置(可加密)
encrypt:
key: ${ENCRYPT_KEY}
spring:
cloud:
config:
token: ${CONFIG_TOKEN}
配置验证与防护:
@Configuration
@ConfigurationProperties(prefix = "app")
@Validated // 启用JSR-303验证
@Data
public class AppConfig {
@NotNull
private String name;
@Min(1)
@Max(65535)
private Integer port;
@Pattern(regexp = "^https?://.*")
private String url;
@Valid // 嵌套验证
private DatabaseConfig database;
@Data
public static class DatabaseConfig {
@NotEmpty
private String host;
@Min(1)
@Max(65535)
private Integer port;
}
}
// 配置健康检查
@Component
public class ConfigurationHealthIndicator implements HealthIndicator {
@Autowired
private AppConfig appConfig;
@Override
public Health health() {
try {
// 验证配置完整性
validateConfiguration();
return Health.up()
.withDetail("configStatus", "VALID")
.build();
} catch (ConfigurationException e) {
return Health.down()
.withDetail("configStatus", "INVALID")
.withDetail("error", e.getMessage())
.build();
}
}
}
🚀 高级配置技巧
动态配置更新机制:
@Configuration
@Slf4j
public class DynamicConfiguration {
@Autowired
private ConfigurableEnvironment environment;
/**
* 运行时更新配置
*/
@Scheduled(fixedRate = 30000) // 每30秒检查一次
public void reloadExternalConfiguration() {
try {
// 从外部源加载最新配置
Map<String, Object> newConfig = loadConfigFromExternalSource();
// 创建新的属性源
MapPropertySource newSource = new MapPropertySource(
"dynamic-config", newConfig);
// 替换或添加属性源
MutablePropertySources sources = environment.getPropertySources();
if (sources.contains("dynamic-config")) {
sources.replace("dynamic-config", newSource);
} else {
sources.addFirst(newSource); // 最高优先级
}
log.info("动态配置更新完成");
} catch (Exception e) {
log.error("动态配置更新失败", e);
}
}
}
📊 配置性能优化
配置加载性能监控:
@Component
@Slf4j
public class ConfigurationPerformanceMonitor {
private long configLoadStartTime;
@EventListener
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
this.configLoadStartTime = System.currentTimeMillis();
}
@EventListener
public void onApplicationEvent(ApplicationReadyEvent event) {
long configLoadTime = System.currentTimeMillis() - configLoadStartTime;
log.info("=== 配置加载性能报告 ===");
log.info("配置加载总耗时: {}ms", configLoadTime);
// 详细性能分析
analyzeConfigurationPerformance(event.getApplicationContext());
}
private void analyzeConfigurationPerformance(ApplicationContext context) {
if (context instanceof ConfigurableApplicationContext) {
ConfigurableEnvironment env = ((ConfigurableApplicationContext) context).getEnvironment();
MutablePropertySources sources = env.getPropertySources();
log.info("加载的配置源数量: {}", sources.size());
sources.forEach(source -> {
if (source instanceof OriginTrackedMapPropertySource) {
log.debug("配置源: {} - 包含 {} 个属性",
source.getName(),
((OriginTrackedMapPropertySource) source).getPropertyNames().length);
}
});
}
}
}
586

被折叠的 条评论
为什么被折叠?



