spring boot 按顺序加载自定义 外部 配置 properties
@Configuration
public class PropertiesConfiguration {
@Bean
public PropertyPlaceholderConfigurer properties() {
final PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
// ppc.setIgnoreUnresolvablePlaceholders(true);
ppc.setIgnoreResourceNotFound(true);
final List<Resource> resourceLst = new ArrayList<Resource>();
resourceLst.add(new ClassPathResource("myapp_base.properties"));
resourceLst.add(new FileSystemResource("/etc/myapp/overriding.propertie"));
resourceLst.add(new ClassPathResource("myapp_test.properties"));
resourceLst.add(new ClassPathResource("myapp_developer_overrides.properties")); // for Developer debugging.
ppc.setLocations(resourceLst.toArray(new Resource[]{}));
return ppc;
}
}
@Configuration
public class PropertiesConfig {
private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
@Value("${application.properties.locations}")
private String[] locations;
@Autowired
private ResourceLoader rl;
@Bean
Map<String, Properties> myProperties() {
return stream(locations)
.collect(toMap(filename -> filename, this::loadProperties));
}
private Properties loadProperties(final String filename) {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
try {
final Resource[] possiblePropertiesResources = ResourcePatternUtils.getResourcePatternResolver(rl).getResources(filename);
final Properties properties = new Properties();
stream(possiblePropertiesResources)
.filter(Resource::exists)
.map(resource1 -> {
try {
return loader.load(resource1.getFilename(), resource1);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).flatMap(l -> l.stream())
.forEach(propertySource -> {
Map source = ((MapPropertySource) propertySource).getSource();
properties.putAll(source);
});
return properties;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Component
public class PropertiesConfigurer extends PropertySourcesPlaceholderConfigurer
implements EnvironmentAware, InitializingBean {
private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfigurer.class);
private String[] locations;
@Autowired
private ResourceLoader rl;
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
// save off Environment for later use
this.environment = environment;
super.setEnvironment(environment);
}
@Override
public void afterPropertiesSet() throws Exception {
// Copy property sources to Environment
MutablePropertySources envPropSources = ((ConfigurableEnvironment) environment).getPropertySources();
envPropSources.forEach(propertySource -> {
if (propertySource.containsProperty("application.properties.locations")) {
locations = ((String) propertySource.getProperty("application.properties.locations")).split(",");
stream(locations).forEach(filename -> loadProperties(filename).forEach(source ->{
envPropSources.addFirst(source);
}));
}
});
}
private List<PropertySource> loadProperties(final String filename) {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
try {
final Resource[] possiblePropertiesResources = ResourcePatternUtils.getResourcePatternResolver(rl).getResources(filename);
final Properties properties = new Properties();
return stream(possiblePropertiesResources)
.filter(Resource::exists)
.map(resource1 -> {
try {
return loader.load(resource1.getFilename(), resource1);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).flatMap(l -> l.stream())
.collect(Collectors.toList());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
我也遇到了同样的问题。我希望能够在启动时用外部文件覆盖内部配置文件,类似于 Spring Boot application.properties 检测。在我的例子中,它是一个 user.properties 文件,其中存储了我的应用程序用户。
我的要求:
从以下位置加载文件(按此顺序)
- 类路径
- 当前目录的/ config子目录。
- 当前目录
- 从启动时命令行参数给出的目录或文件位置
我想出了以下解决方案:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Properties;
import static java.util.Arrays.stream;
@Configuration
public class PropertiesConfig {
private static final Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
private final static String PROPERTIES_FILENAME = "user.properties";
@Value("${properties.location:}")
private String propertiesLocation;
@Bean
Properties userProperties() throws IOException {
final Resource[] possiblePropertiesResources = {
new ClassPathResource(PROPERTIES_FILENAME),
new PathResource("config/" + PROPERTIES_FILENAME),
new PathResource(PROPERTIES_FILENAME),
new PathResource(getCustomPath())
};
// Find the last existing properties location to emulate spring boot application.properties discovery
final Resource propertiesResource = stream(possiblePropertiesResources)
.filter(Resource::exists)
.reduce((previous, current) -> current)
.get();
final Properties userProperties = new Properties();
userProperties.load(propertiesResource.getInputStream());
LOG.info("Using {} as user resource", propertiesResource);
return userProperties;
}
private String getCustomPath() {
return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + PROPERTIES_FILENAME;
}
}
现在应用程序使用类路径资源,但也检查其他给定位置的资源。将挑选并使用最后一个存在的资源。我能够使用 java -jar myapp.jar --properties.location=/directory/myproperties.properties 启动我的应用程序,以使用让我满意的属性位置。
这里有一个重要的细节:使用空字符串作为@Value注释中properties.location的默认值,以避免未设置属性时出现错误。
properties.location 的约定是:使用目录或属性文件的路径作为 properties.location。
如果您只想覆盖特定属性,可以使用具有 setIgnoreResourceNotFound(true) 的 PropertiesFactoryBean 并将资源数组设置为位置。
我确信这个解决方案可以扩展以处理多个文件......
编辑
这里是我针对多个文件的解决方案:) 像以前一样,这可以与 PropertiesFactoryBean 结合使用。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.PathResource;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;
@Configuration
class PropertiesConfig {
private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
private final static String[] PROPERTIES_FILENAMES = {"job1.properties", "job2.properties", "job3.properties"};
@Value("${properties.location:}")
private String propertiesLocation;
@Bean
Map<String, Properties> myProperties() {
return stream(PROPERTIES_FILENAMES)
.collect(toMap(filename -> filename, this::loadProperties));
}
private Properties loadProperties(final String filename) {
final Resource[] possiblePropertiesResources = {
new ClassPathResource(filename),
new PathResource("config/" + filename),
new PathResource(filename),
new PathResource(getCustomPath(filename))
};
final Resource resource = stream(possiblePropertiesResources)
.filter(Resource::exists)
.reduce((previous, current) -> current)
.get();
final Properties properties = new Properties();
try {
properties.load(resource.getInputStream());
} catch(final IOException exception) {
throw new RuntimeException(exception);
}
LOG.info("Using {} as user resource", resource);
return properties;
}
private String getCustomPath(final String filename) {
return propertiesLocation.endsWith(".properties") ? propertiesLocation : propertiesLocation + filename;
}
}
@mxsb 解决方案的修改版本允许我们定义多个文件,在我的情况下,这些是 yml 文件。
在我的 application-dev.yml 中,我添加了此配置,允许我注入所有包含 -dev.yml 的 yml。这也可以是特定文件的列表。“classpath:/test/test.yml,classpath:/test2/test.yml”
application:
properties:
locations: "classpath*:/**/*-dev.yml"
这有助于获取属性图。
@Configuration
public class PropertiesConfig {
private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfig.class);
@Value("${application.properties.locations}")
private String[] locations;
@Autowired
private ResourceLoader rl;
@Bean
Map<String, Properties> myProperties() {
return stream(locations)
.collect(toMap(filename -> filename, this::loadProperties));
}
private Properties loadProperties(final String filename) {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
try {
final Resource[] possiblePropertiesResources = ResourcePatternUtils.getResourcePatternResolver(rl).getResources(filename);
final Properties properties = new Properties();
stream(possiblePropertiesResources)
.filter(Resource::exists)
.map(resource1 -> {
try {
return loader.load(resource1.getFilename(), resource1);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).flatMap(l -> l.stream())
.forEach(propertySource -> {
Map source = ((MapPropertySource) propertySource).getSource();
properties.putAll(source);
});
return properties;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
但是,如果像我的情况一样,我希望为每个配置文件拆分 yml 文件并在 bean 初始化之前加载它们并将其直接注入到 spring 配置中。
config
- application.yml
- application-dev.yml
- application-prod.yml
management
- management-dev.yml
- management-prod.yml
...你明白了
组件略有不同
@Component
public class PropertiesConfigurer extends PropertySourcesPlaceholderConfigurer
implements EnvironmentAware, InitializingBean {
private final static Logger LOG = LoggerFactory.getLogger(PropertiesConfigurer.class);
private String[] locations;
@Autowired
private ResourceLoader rl;
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
// save off Environment for later use
this.environment = environment;
super.setEnvironment(environment);
}
@Override
public void afterPropertiesSet() throws Exception {
// Copy property sources to Environment
MutablePropertySources envPropSources = ((ConfigurableEnvironment) environment).getPropertySources();
envPropSources.forEach(propertySource -> {
if (propertySource.containsProperty("application.properties.locations")) {
locations = ((String) propertySource.getProperty("application.properties.locations")).split(",");
stream(locations).forEach(filename -> loadProperties(filename).forEach(source ->{
envPropSources.addFirst(source);
}));
}
});
}
Externalized Configuration :: Spring Boot
-
Properties转换成Map
-
-
import java.util.HashMap;
-
import java.util.Map;
-
import java.util.Properties;
-
import java.util.Set;
-
-
public class Test {
-
-
public static void main(String args[]){
-
Properties properties = new Properties();
-
-
properties.setProperty("StrictHostKeyChecking", "no");
-
properties.setProperty("app.version", "1.0");
-
-
Map<String, String> map = new HashMap<String, String>((Map) properties);
-
-
Set propertySet = map.entrySet();
-
for (Object o : propertySet) {
-
Map.Entry entry = (Map.Entry) o;
-
System.out.printf("%s = %s%n", entry.getKey(), entry.getValue());
-
}
-
}
-
}
代码解释
其实还有更简单的办法,因为Properties继承了HashTable实现了Map,所以Properties也有entrySet()方法,不需要转成Map再调用Map的entrySet()方法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律