diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index 573339edf9..62500e447d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -155,44 +155,56 @@ class MapBinder extends AggregateBinder> { private final ResolvableType valueType; + private final Class resolvedValueType; + + private final boolean valueTreatedAsNestedMap; + + private final Bindable bindableMapType; + + private final Bindable bindableValueType; + EntryBinder(ConfigurationPropertyName root, Bindable target, AggregateElementBinder elementBinder) { this.root = root; this.elementBinder = elementBinder; this.mapType = target.getType().asMap(); this.keyType = this.mapType.getGeneric(0); this.valueType = this.mapType.getGeneric(1); + this.resolvedValueType = this.valueType.resolve(Object.class); + this.valueTreatedAsNestedMap = Object.class.equals(this.resolvedValueType); + this.bindableMapType = Bindable.of(this.mapType); + this.bindableValueType = Bindable.of(this.valueType); } void bindEntries(ConfigurationPropertySource source, Map map) { if (source instanceof IterableConfigurationPropertySource iterableSource) { for (ConfigurationPropertyName name : iterableSource) { - Bindable valueBindable = getValueBindable(name); ConfigurationPropertyName entryName = getEntryName(source, name); Object key = getContext().getConverter().convert(getKeyName(entryName), this.keyType); + Bindable valueBindable = getValueBindable(name); map.computeIfAbsent(key, (k) -> this.elementBinder.bind(entryName, valueBindable)); } } } private Bindable getValueBindable(ConfigurationPropertyName name) { - if (!this.root.isParentOf(name) && isValueTreatedAsNestedMap()) { - return Bindable.of(this.mapType); - } - return Bindable.of(this.valueType); + return (!isParentOf(name) && this.valueTreatedAsNestedMap) ? this.bindableMapType : this.bindableValueType; } private ConfigurationPropertyName getEntryName(ConfigurationPropertySource source, ConfigurationPropertyName name) { - Class resolved = this.valueType.resolve(Object.class); - if (Collection.class.isAssignableFrom(resolved) || this.valueType.isArray()) { + if (Collection.class.isAssignableFrom(this.resolvedValueType) || this.valueType.isArray()) { return chopNameAtNumericIndex(name); } - if (!this.root.isParentOf(name) && (isValueTreatedAsNestedMap() || !isScalarValue(source, name))) { + if (!isParentOf(name) && (this.valueTreatedAsNestedMap || !isScalarValue(source, name))) { return name.chop(this.root.getNumberOfElements() + 1); } return name; } + private boolean isParentOf(ConfigurationPropertyName name) { + return this.root.isParentOf(name); + } + private ConfigurationPropertyName chopNameAtNumericIndex(ConfigurationPropertyName name) { int start = this.root.getNumberOfElements() + 1; int size = name.getNumberOfElements(); @@ -204,10 +216,6 @@ class MapBinder extends AggregateBinder> { return name; } - private boolean isValueTreatedAsNestedMap() { - return Object.class.equals(this.valueType.resolve(Object.class)); - } - private boolean isScalarValue(ConfigurationPropertySource source, ConfigurationPropertyName name) { Class resolved = this.valueType.resolve(Object.class); if (!resolved.getName().startsWith("java.lang") && !resolved.isEnum()) {