diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java index 99e79a4af4f..5d7cdf73cae 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.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. @@ -86,22 +86,34 @@ public class ResourceBanner implements Banner { * @return a mutable list of property resolvers */ protected List getPropertyResolvers(Environment environment, Class sourceClass) { - MutablePropertySources sources = new MutablePropertySources(); - if (environment instanceof ConfigurableEnvironment configurableEnvironment) { - configurableEnvironment.getPropertySources().forEach(sources::addLast); - } - sources.addLast(getTitleSource(sourceClass)); - sources.addLast(getAnsiSource()); - sources.addLast(getVersionSource(sourceClass, environment)); List resolvers = new ArrayList<>(); - resolvers.add(new PropertySourcesPropertyResolver(sources)); + resolvers.add(new PropertySourcesPropertyResolver(createNullDefaultSources(environment, sourceClass))); + resolvers.add(new PropertySourcesPropertyResolver(createEmptyDefaultSources(environment, sourceClass))); return resolvers; } - private MapPropertySource getTitleSource(Class sourceClass) { + private MutablePropertySources createNullDefaultSources(Environment environment, Class sourceClass) { + MutablePropertySources nullDefaultSources = new MutablePropertySources(); + if (environment instanceof ConfigurableEnvironment configurableEnvironment) { + configurableEnvironment.getPropertySources().forEach(nullDefaultSources::addLast); + } + nullDefaultSources.addLast(getTitleSource(sourceClass, null)); + nullDefaultSources.addLast(getAnsiSource()); + nullDefaultSources.addLast(getVersionSource(sourceClass, environment, null)); + return nullDefaultSources; + } + + private MutablePropertySources createEmptyDefaultSources(Environment environment, Class sourceClass) { + MutablePropertySources emptyDefaultSources = new MutablePropertySources(); + emptyDefaultSources.addLast(getTitleSource(sourceClass, "")); + emptyDefaultSources.addLast(getVersionSource(sourceClass, environment, "")); + return emptyDefaultSources; + } + + private MapPropertySource getTitleSource(Class sourceClass, String defaultValue) { String applicationTitle = getApplicationTitle(sourceClass); Map titleMap = Collections.singletonMap("application.title", - (applicationTitle != null) ? applicationTitle : ""); + (applicationTitle != null) ? applicationTitle : defaultValue); return new MapPropertySource("title", titleMap); } @@ -120,21 +132,21 @@ public class ResourceBanner implements Banner { return new AnsiPropertySource("ansi", true); } - private MapPropertySource getVersionSource(Class sourceClass, Environment environment) { - return new MapPropertySource("version", getVersionsMap(sourceClass, environment)); + private MapPropertySource getVersionSource(Class sourceClass, Environment environment, String defaultValue) { + return new MapPropertySource("version", getVersionsMap(sourceClass, environment, defaultValue)); } - private Map getVersionsMap(Class sourceClass, Environment environment) { + private Map getVersionsMap(Class sourceClass, Environment environment, String defaultValue) { String appVersion = getApplicationVersion(sourceClass); if (appVersion == null) { appVersion = getApplicationVersion(environment); } String bootVersion = getBootVersion(); Map versions = new HashMap<>(); - versions.put("application.version", getVersionString(appVersion, false)); - versions.put("spring-boot.version", getVersionString(bootVersion, false)); - versions.put("application.formatted-version", getVersionString(appVersion, true)); - versions.put("spring-boot.formatted-version", getVersionString(bootVersion, true)); + 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)); + versions.put("spring-boot.formatted-version", getVersionString(bootVersion, true, defaultValue)); return versions; } @@ -157,9 +169,9 @@ public class ResourceBanner implements Banner { return SpringBootVersion.getVersion(); } - private String getVersionString(String version, boolean format) { + private String getVersionString(String version, boolean format, String fallback) { if (version == null) { - return ""; + return fallback; } return format ? " (v" + version + ")" : version; } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.java index b7de1635a5a..91959459fc0 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.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. @@ -68,6 +68,14 @@ class ResourceBannerTests { assertThat(banner).startsWith("banner 1 "); } + @Test + void renderWithoutVersionsWithDefaultValues() { + Resource resource = new ByteArrayResource( + "banner ${a} ${spring-boot.version:X.Y.Z} ${application.version:A.B.C}".getBytes()); + String banner = printBanner(resource, null, null, null); + assertThat(banner).startsWith("banner 1 X.Y.Z A.B.C"); + } + @Test void renderFormattedVersions() { Resource resource = new ByteArrayResource( @@ -79,9 +87,18 @@ class ResourceBannerTests { @Test void renderWithoutFormattedVersions() { Resource resource = new ByteArrayResource( - "banner ${a}${spring-boot.formatted-version}${application.formatted-version}".getBytes()); + "banner ${a} ${spring-boot.formatted-version} ${application.formatted-version}".getBytes()); String banner = printBanner(resource, null, null, null); - assertThat(banner).startsWith("banner 1"); + assertThat(banner).startsWith("banner 1 "); + } + + @Test + void renderWithoutFormattedVersionsWithDefaultValues() { + Resource resource = new ByteArrayResource( + "banner ${a} ${spring-boot.formatted-version:(vX.Y.Z)} ${application.formatted-version:(vA.B.C)}" + .getBytes()); + String banner = printBanner(resource, null, null, null); + assertThat(banner).startsWith("banner 1 (vX.Y.Z) (vA.B.C)"); } @Test @@ -130,6 +147,13 @@ class ResourceBannerTests { assertThat(banner).startsWith("banner 1"); } + @Test + void renderWithoutTitleWithDefaultValue() { + Resource resource = new ByteArrayResource("banner ${application.title:Default Title} ${a}".getBytes()); + String banner = printBanner(resource, null, null, null); + assertThat(banner).startsWith("banner Default Title 1"); + } + @Test void renderWithDefaultValues() { Resource resource = new ByteArrayResource(