Spring Environment:能否控制systemProperties与systemEnvironment的解析顺序?
默认情况下的输出结果
先给你明确答案:当你同时设置OS环境变量key=os和通过java -Dkey=system指定JVM系统属性时,默认调用Bean的方法获取key会返回system。
这是因为Spring Environment的内置PropertySources有固定优先级:systemProperties(对应JVM系统属性,即-D参数)的优先级高于systemEnvironment(对应操作系统环境变量)。同名key存在时,优先级高的配置源会覆盖低的。
能不能控制PropertySources的解析顺序?
当然可以!Spring没有把这个顺序写死,提供了多种灵活的方式调整配置源的优先级,不管是内置的还是自定义的配置源,都能重新排序。
如何调整systemEnvironment和systemProperties的扫描顺序?
下面是几种常用且靠谱的方法,按推荐程度排序:
1. 实现EnvironmentPostProcessor(最推荐)
这种方式能在Spring容器启动的最早期调整配置源顺序,避免后续Bean初始化时读取到旧的优先级配置。
先写一个自定义处理器:
import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; public class CustomEnvOrderProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 获取目标配置源 PropertySource<?> systemProps = environment.getPropertySources().get("systemProperties"); PropertySource<?> systemEnv = environment.getPropertySources().get("systemEnvironment"); if (systemProps != null && systemEnv != null) { // 先移除systemEnvironment,再把它添加到systemProperties前面,提升优先级 environment.getPropertySources().remove("systemEnvironment"); environment.getPropertySources().addBefore("systemProperties", systemEnv); } } }
然后在src/main/resources/META-INF/spring.factories里注册这个处理器,Spring Boot启动时会自动加载:
org.springframework.boot.env.EnvironmentPostProcessor=com.your.package.CustomEnvOrderProcessor
调整后再启动程序,key的值就会变成os,因为环境变量的优先级现在高于系统属性了。
2. 在Bean中用@PostConstruct调整
如果是在容器启动后调整(不推荐,因为部分Bean可能已经读取了配置),可以在一个组件类里注入ConfigurableEnvironment,然后在初始化方法里修改:
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @Component public class PropertyOrderAdjuster { private final ConfigurableEnvironment environment; // 构造注入Environment public PropertyOrderAdjuster(ConfigurableEnvironment environment) { this.environment = environment; } @PostConstruct public void adjustOrder() { PropertySource<?> systemProps = environment.getPropertySources().get("systemProperties"); PropertySource<?> systemEnv = environment.getPropertySources().get("systemEnvironment"); if (systemProps != null && systemEnv != null) { environment.getPropertySources().remove("systemEnvironment"); environment.getPropertySources().addBefore("systemProperties", systemEnv); } } }
3. 在SpringApplication启动时直接修改
如果你是通过SpringApplication手动启动应用,也可以在启动前添加初始化器来调整:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; @SpringBootApplication public class YourApp { public static void main(String[] args) { SpringApplication app = new SpringApplication(YourApp.class); // 添加环境初始化器 app.addInitializers(context -> { ConfigurableEnvironment env = context.getEnvironment(); PropertySource<?> systemProps = env.getPropertySources().get("systemProperties"); PropertySource<?> systemEnv = env.getPropertySources().get("systemEnvironment"); if (systemProps != null && systemEnv != null) { env.getPropertySources().remove("systemEnvironment"); env.getPropertySources().addBefore("systemProperties", systemEnv); } }); app.run(args); } }
小提示
调整顺序时,要熟悉PropertySources的几个核心方法:
addFirst():把配置源放到最前面(优先级最高)addLast():放到最后面(优先级最低)addBefore(String relativePropertySourceName, PropertySource<?> propertySource):放到指定配置源的前面addAfter(String relativePropertySourceName, PropertySource<?> propertySource):放到指定配置源的后面
合理使用这些方法,就能完全掌控配置源的优先级顺序。
内容的提问来源于stack exchange,提问作者qwazer

