From 6723cfd809e1dffac97b13149f61d6e23c5cfd1c Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 28 Aug 2025 09:59:08 +0200 Subject: [PATCH] Improve null-safety of core/spring-boot See gh-46926 --- .../springframework/boot/ResourceBanner.java | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/core/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java b/core/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java index f5ae815b4ee..5807f168a7d 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java @@ -32,6 +32,7 @@ import org.jspecify.annotations.Nullable; import org.springframework.boot.ansi.AnsiPropertySource; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; @@ -41,6 +42,7 @@ import org.springframework.core.io.Resource; import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; import org.springframework.util.StreamUtils; +import org.springframework.util.StringUtils; /** * Banner implementation that prints from a source text {@link Resource}. @@ -112,11 +114,11 @@ public class ResourceBanner implements Banner { return emptyDefaultSources; } - private MapPropertySource getTitleSource(@Nullable Class sourceClass, @Nullable String defaultValue) { + private MapWithNullsPropertySource getTitleSource(@Nullable Class sourceClass, @Nullable String defaultValue) { String applicationTitle = getApplicationTitle(sourceClass); - Map titleMap = Collections.singletonMap("application.title", + Map titleMap = Collections.singletonMap("application.title", (applicationTitle != null) ? applicationTitle : defaultValue); - return new MapPropertySource("title", titleMap); + return new MapWithNullsPropertySource("title", titleMap); } /** @@ -134,14 +136,14 @@ public class ResourceBanner implements Banner { return new AnsiPropertySource("ansi", true); } - private MapPropertySource getVersionSource(Environment environment, @Nullable String defaultValue) { - return new MapPropertySource("version", getVersionsMap(environment, defaultValue)); + private MapWithNullsPropertySource getVersionSource(Environment environment, @Nullable String defaultValue) { + return new MapWithNullsPropertySource("version", getVersionsMap(environment, defaultValue)); } - private Map getVersionsMap(Environment environment, @Nullable String defaultValue) { + private Map getVersionsMap(Environment environment, @Nullable String defaultValue) { String appVersion = getApplicationVersion(environment); String bootVersion = getBootVersion(); - Map versions = new HashMap<>(); + Map versions = new HashMap<>(); versions.put("application.version", getVersionString(appVersion, false, defaultValue)); versions.put("spring-boot.version", getVersionString(bootVersion, false, defaultValue)); versions.put("application.formatted-version", getVersionString(appVersion, true, defaultValue)); @@ -164,4 +166,30 @@ public class ResourceBanner implements Banner { return format ? " (v" + version + ")" : version; } + /** + * Like {@link MapPropertySource}, but allows {@code null} as map values. + */ + private static class MapWithNullsPropertySource extends EnumerablePropertySource> { + + MapWithNullsPropertySource(String name, Map source) { + super(name, source); + } + + @Override + public String[] getPropertyNames() { + return StringUtils.toStringArray(this.source.keySet()); + } + + @Override + public @Nullable Object getProperty(String name) { + return this.source.get(name); + } + + @Override + public boolean containsProperty(String name) { + return this.source.containsKey(name); + } + + } + }