Java中如何用@PropertySource结合占位符加载子目录配置文件
我来帮你搞定这个配置加载的问题!首先,你遇到的两个核心问题:一是子目录配置加载失败,二是想用占位符动态指定目录,我分别给你梳理解决方案:
一、先排查子目录配置加载失败的原因
你之前写的@PropertySource({"classpath:prod/application.properties", ...})加载失败,大概率是打包时子目录的配置文件没被正确复制到classpath里。可以检查下Maven/Gradle的资源配置:
Maven配置示例
确保pom.xml里的resources插件包含所有子目录的.properties文件:
<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> </includes> </resource> </resources> </build>
这样打包时,prod、prod-brand-1这些子目录下的配置会被完整复制到target/classes目录,Spring就能正确找到它们。
二、用占位符动态指定目录的方案
@PropertySource本身支持${...}占位符,但要注意占位符的解析依赖于已初始化的Environment,所以需要配合PropertySourcesPlaceholderConfigurer来提前处理占位符,同时利用环境变量传入动态目录值。
方案1:单动态目录加载(适合只需要加载一个子目录的场景)
@Configuration // 先加载默认配置,确保基础配置可用 @PropertySource("classpath:application-default.properties") // 用环境变量env.config.dir动态指定子目录,ignoreResourceNotFound避免目录不存在时报错 @PropertySource(value = "classpath:${env.config.dir}/application.properties", ignoreResourceNotFound = true) public class PropertyConfig { // 必须声明为static Bean,这样它会在@PropertySource解析前初始化,才能处理占位符 @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } // 你的业务逻辑代码 }
使用时,只需要在启动项目时传入环境变量env.config.dir=prod(或者prod-brand-1、prod-brand-2),Spring就会自动加载对应目录下的配置文件。
方案2:多动态目录加载(适合需要同时加载多个子目录的场景)
如果需要一次性加载多个子目录的配置,比如同时加载prod和prod-brand-1,可以通过EnvironmentPostProcessor来实现更灵活的动态加载:
步骤1:实现EnvironmentPostProcessor
import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.boot.env.PropertiesPropertySourceLoader; import org.springframework.boot.SpringApplication; import java.io.IOException; import java.util.List; public class DynamicConfigLoader implements EnvironmentPostProcessor { private final PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader(); @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 从环境变量获取需要加载的目录列表,用逗号分隔,比如config.dirs=prod,prod-brand-1 String dirs = environment.getProperty("config.dirs"); if (dirs != null) { for (String dir : dirs.split(",")) { String dirPath = dir.trim() + "/application.properties"; Resource resource = new ClassPathResource(dirPath); if (resource.exists()) { try { List<PropertySource<?>> sources = loader.load("dynamic-config-" + dir, resource); sources.forEach(source -> environment.getPropertySources().addLast(source)); } catch (IOException e) { throw new RuntimeException("加载配置文件失败: " + dirPath, e); } } } } // 加载默认配置(优先级低于动态加载的配置,可根据需求调整addFirst/addLast) Resource defaultResource = new ClassPathResource("application-default.properties"); if (defaultResource.exists()) { try { List<PropertySource<?>> defaultSources = loader.load("default-config", defaultResource); defaultSources.forEach(source -> environment.getPropertySources().addFirst(source)); } catch (IOException e) { throw new RuntimeException("加载默认配置失败", e); } } } }
步骤2:注册自定义EnvironmentPostProcessor
在src/main/resources/META-INF/spring.factories文件中添加以下内容,让Spring启动时自动识别这个处理器:
org.springframework.boot.env.EnvironmentPostProcessor=com.yourpackage.DynamicConfigLoader
使用方式
启动项目时传入环境变量config.dirs=prod,prod-brand-1,prod-brand-2,Spring就会自动加载这三个目录下的配置文件。
三、优先级说明
- 如果多个配置文件有相同的key,后加载的配置会覆盖先加载的。比如方案2中我们把默认配置用
addFirst加到最前面,动态配置用addLast加到后面,这样动态配置的优先级更高,符合实际业务需求。
内容的提问来源于stack exchange,提问作者Sid

