如何在Spring Profile的@PropertySource处理后执行EnvironmentPostProcessor
问题根源分析
你遇到的核心问题是执行时机不匹配:EnvironmentPostProcessor是在Spring Boot启动的prepareEnvironment阶段执行的,此时ApplicationContext还未创建,带@Profile("development")和@PropertySource的DevelopmentConfiguration类还没被解析,那些properties文件里的属性根本没加载到Environment中,自然拿不到。
下面提供两种实用解决方案:
方案一:在EnvironmentPostProcessor中手动加载Profile对应的PropertySource
既然Spring Boot还没加载这些文件,我们可以在处理器里检测到development Profile激活时,主动把这些PropertySource加载到Environment中,之后再执行属性替换逻辑。
代码实现
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { private static final String DEVELOPMENT_PROFILE = "development"; private final PropertySourceLoader propertySourceLoader = new PropertiesPropertySourceLoader(); @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 检查是否激活了development Profile if (Arrays.asList(environment.getActiveProfiles()).contains(DEVELOPMENT_PROFILE)) { try { // 依次加载三个配置文件 loadPropertySource(environment, new ClassPathResource("development.properties"), "dev-classpath"); loadPropertySource(environment, new ClassPathResource("development-logging.properties"), "dev-log-classpath"); loadPropertySource(environment, new FileSystemResource("./local-development-configuration.properties"), "dev-local-file"); } catch (IOException e) { throw new IllegalStateException("加载development Profile配置文件失败", e); } // 执行你的属性替换逻辑,此时已能获取到上述文件的属性 replaceTargetProperties(environment); } } private void loadPropertySource(ConfigurableEnvironment environment, Resource resource, String sourceName) throws IOException { if (resource.exists()) { List<PropertySource<?>> sources = propertySourceLoader.load(sourceName, resource); sources.forEach(source -> environment.getPropertySources().addLast(source)); } } private void replaceTargetProperties(ConfigurableEnvironment environment) { // 示例:根据源属性替换目标属性 String sourceValue = environment.getProperty("app.source.property"); if (sourceValue != null) { ((MutablePropertySources) environment.getPropertySources()).addFirst( new MapPropertySource("custom-property-override", Collections.singletonMap("app.target.property", sourceValue)) ); } } @Override public int getOrder() { // 设置执行顺序,确保在Spring Boot默认处理器之后,ApplicationContext创建之前 return Ordered.LOWEST_PRECEDENCE - 10; } }
注册处理器
在项目的META-INF/spring.factories文件中添加以下配置,让Spring Boot识别这个处理器:
org.springframework.boot.env.EnvironmentPostProcessor=com.your.package.CustomEnvironmentPostProcessor
方案二:改用ApplicationContextInitializer(适合时机要求宽松的场景)
如果你的属性替换不需要在最早期的环境准备阶段完成,可以改用ApplicationContextInitializer——它会在ApplicationContext创建完成但未执行refresh时触发,此时@PropertySource的属性已经加载到Environment中了。
代码实现
public class CustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { private static final String DEVELOPMENT_PROFILE = "development"; @Override public void initialize(ConfigurableApplicationContext applicationContext) { ConfigurableEnvironment environment = applicationContext.getEnvironment(); if (Arrays.asList(environment.getActiveProfiles()).contains(DEVELOPMENT_PROFILE)) { // 直接执行属性替换,此时已能获取到DevelopmentConfiguration加载的属性 replaceTargetProperties(environment); } } private void replaceTargetProperties(ConfigurableEnvironment environment) { // 这里写你的属性替换逻辑,和方案一的替换逻辑一致即可 String sourceValue = environment.getProperty("app.source.property"); if (sourceValue != null) { ((MutablePropertySources) environment.getPropertySources()).addFirst( new MapPropertySource("custom-property-override", Collections.singletonMap("app.target.property", sourceValue)) ); } } }
注册初始化器
同样在META-INF/spring.factories中配置:
org.springframework.context.ApplicationContextInitializer=com.your.package.CustomApplicationContextInitializer
方案选择建议
- 方案一:属性替换时机早,所有依赖环境属性的Bean初始化时都能拿到替换后的值,适合对属性生效时机要求严格的场景;
- 方案二:无需手动加载配置文件,实现更简单,但时机稍晚,适合对属性生效时机要求不高的场景。
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

