Spring Boot中如何通过Environment动态获取Map类型配置?
解决Spring Boot动态获取Map类型配置的问题
问题本质
通过@Value("#{${app.sftp.basic.defaultFields}}")结合SpEL能正常加载Map,是因为SpEL会自动解析配置字符串为Map结构;但调用Environment.getProperty(defaultFieldsKey, Map.class)时,Spring默认并未注册String到Map的类型转换器,因此抛出ConverterNotFoundException。
可行解决方案
方案1:自定义类型转换器(推荐)
实现Spring的Converter<String, Map<String, String>>接口,将配置文件中的字符串格式(如k1=v1,k2=v2或{k1=v1,k2=v2})转换为Map,再注册到Spring的ConversionService中即可全局生效。
示例转换器代码:
import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; import java.util.LinkedHashMap; import java.util.Map; import java.util.Arrays; import java.util.stream.Collectors; @Component public class StringToMapConverter implements Converter<String, Map<String, String>> { @Override public Map<String, String> convert(String source) { if (source == null || source.trim().isEmpty()) { return new LinkedHashMap<>(); } // 兼容配置文件中带{}的格式,先清理符号再拆分 String cleanedContent = source.replaceAll("[{}]", "").trim(); return Arrays.stream(cleanedContent.split(",")) .map(entry -> entry.split("=", 2)) .collect(Collectors.toMap( parts -> parts[0].trim(), parts -> parts.length > 1 ? parts[1].trim() : "", (oldVal, newVal) -> newVal, // 处理重复键,保留新值 LinkedHashMap::new )); } }
注册转换器(Spring Boot 2.x+会自动扫描并注册Converter Bean,若需手动配置可添加以下类):
import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { private final StringToMapConverter stringToMapConverter; public WebConfig(StringToMapConverter stringToMapConverter) { this.stringToMapConverter = stringToMapConverter; } @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(stringToMapConverter); } }
完成后,调用Environment.getProperty(defaultFieldsKey, Map.class)即可正常转换。
方案2:手动解析字符串(临时场景)
若不想自定义转换器,可直接获取配置字符串后手动解析,适合简单临时场景:
String configStr = environment.getProperty(defaultFieldsKey); Map<String, String> resultMap = new LinkedHashMap<>(); if (configStr != null && !configStr.isEmpty()) { String cleaned = configStr.replaceAll("[{}]", "").trim(); for (String entry : cleaned.split(",")) { String[] keyValuePair = entry.split("=", 2); if (keyValuePair.length == 2) { resultMap.put(keyValuePair[0].trim(), keyValuePair[1].trim()); } } }
这种方式复用性差,仅适合快速解决单一场景问题。
方案3:使用@ConfigurationProperties(固定配置结构场景)
如果配置键是提前确定的,可通过@ConfigurationProperties绑定配置类,直接注入获取Map:
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Map; @Component @ConfigurationProperties(prefix = "app.sftp.basic") public class SftpConfigProperties { private Map<String, String> defaultFields; // Getter & Setter public Map<String, String> getDefaultFields() { return defaultFields; } public void setDefaultFields(Map<String, String> defaultFields) { this.defaultFields = defaultFields; } }
注入该类后直接调用getDefaultFields()即可获取Map,但此方案不支持完全动态的配置键(键名需提前定义)。
总结
- 若需要动态指定配置键获取Map,自定义Converter是最规范、可复用的方案;
- 临时场景可手动解析字符串;
- 配置结构固定时,
@ConfigurationProperties更简洁高效。
内容的提问来源于stack exchange,提问作者Fluppe
相关产品推荐
相关产品推荐

