Environment.getProperties加载YML Map失败?与@ConfigurationProperties差异及解决
一、核心差异
属性匹配逻辑不同
@ConfigurationProperties支持分层前缀匹配,比如你配置里的class.student,只要指定prefix="class.student",就能自动识别嵌套结构并绑定成对应类型。而Environment的getProperties是精确匹配单个属性键,你传"student"的话,它只会找键名就是"student"的顶层属性,根本不会去class节点下查找,自然返回null。底层绑定机制不同
@ConfigurationProperties依赖Spring的Binder机制,专门处理复杂类型(Map、自定义类)的绑定,会自动解析配置的分层结构,把嵌套键值对转换成对应数据结构。而Environment.getProperties只是基于PropertySource做简单的键值查找,没有内置嵌套解析和复杂类型转换的逻辑,设计初衷就是快速获取单个简单属性值。功能复杂度不同
@ConfigurationProperties支持多PropertySource整合、类型转换、默认值设置、参数校验等高级特性;Environment虽然聚合了所有配置源,但getProperties在处理复杂类型时能力有限,不会主动做额外的解析处理。
二、通过Environment正确获取student节点为Map的方法
要拿到嵌套的student节点,得用完整的配置路径,并且借助Spring的Binder来解析(和@ConfigurationProperties用的是同一套逻辑),具体步骤:
- 注入Environment和Binder
@Autowired private Environment environment; @Autowired private Binder binder;
- 用Binder绑定完整路径的配置为Map
// 指定完整路径class.student,绑定成Map Map<String, Object> studentMap = binder.bind("class.student", Map.class).orElse(null);
如果不想用Binder,也可以手动遍历Environment的配置源,筛选出class.student.开头的属性,自己组装成Map:
Map<String, Object> studentMap = new HashMap<>(); ConfigurableEnvironment configurableEnv = (ConfigurableEnvironment) environment; for (PropertySource<?> source : configurableEnv.getPropertySources()) { if (source instanceof EnumerablePropertySource) { for (String key : ((EnumerablePropertySource<?>) source).getPropertyNames()) { if (key.startsWith("class.student.")) { String mapKey = key.substring("class.student.".length()); studentMap.put(mapKey, source.getProperty(key)); } } } }
显然第一种用Binder的方式更简洁,也更符合Spring的设计规范。
补充
你直接调用environment.getProperties("student", Map.class)返回null的原因很简单:你的配置里根本没有顶层的"student"属性,它是嵌套在"class"下的子节点,Environment找不到对应的键,所以返回null。而@ConfigurationProperties通过prefix指定了"class",会自动扫描该前缀下的所有子属性并完成绑定。
内容的提问来源于stack exchange,提问作者gluttony

