diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/DurationFormat.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/DurationFormat.java index 116a780833d3..3f53a2368bb8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/DurationFormat.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/DurationFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -30,7 +30,7 @@ * @author Phillip Webb * @since 2.0.0 */ -@Target(ElementType.FIELD) +@Target({ ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DurationFormat { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java index 45d45183b812..48f6537dbce8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java @@ -31,7 +31,7 @@ * @author Edson Chávez * @since 2.3.0 */ -@Target(ElementType.FIELD) +@Target({ ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface PeriodFormat { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java index e1e289baf67e..02c50d918142 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java @@ -56,7 +56,11 @@ import org.springframework.boot.context.properties.bind.validation.BindValidationException; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.convert.DataSizeUnit; +import org.springframework.boot.convert.DurationFormat; +import org.springframework.boot.convert.DurationStyle; import org.springframework.boot.convert.DurationUnit; +import org.springframework.boot.convert.PeriodFormat; +import org.springframework.boot.convert.PeriodStyle; import org.springframework.boot.convert.PeriodUnit; import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.OutputCaptureExtension; @@ -808,6 +812,51 @@ void loadWhenBindingToConstructorParametersWithDefaultDataUnitShouldBind() { assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(4)); } + @Test + void loadWhenBindingToConstructorParametersWithCustomDataFormatShouldBind() { + MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); + Map source = new HashMap<>(); + source.put("test.duration", "12d"); + source.put("test.period", "13y"); + sources.addLast(new MapPropertySource("test", source)); + load(ConstructorParameterWithFormatConfiguration.class); + ConstructorParameterWithFormatProperties bean = this.context + .getBean(ConstructorParameterWithFormatProperties.class); + assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(12)); + assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(13)); + } + + @Test + void loadWhenBindingToConstructorParametersWithNotMatchingCustomDurationFormatShouldFail() { + MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); + Map source = new HashMap<>(); + source.put("test.duration", "P12D"); + sources.addLast(new MapPropertySource("test", source)); + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> load(ConstructorParameterWithFormatConfiguration.class)).havingCause() + .isInstanceOf(BindException.class); + } + + @Test + void loadWhenBindingToConstructorParametersWithNotMatchingCustomPeriodFormatShouldFail() { + MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); + Map source = new HashMap<>(); + source.put("test.period", "P12D"); + sources.addLast(new MapPropertySource("test", source)); + assertThatExceptionOfType(Exception.class) + .isThrownBy(() -> load(ConstructorParameterWithFormatConfiguration.class)).havingCause() + .isInstanceOf(BindException.class); + } + + @Test + void loadWhenBindingToConstructorParametersWithDefaultDataFormatShouldBind() { + load(ConstructorParameterWithFormatConfiguration.class); + ConstructorParameterWithFormatProperties bean = this.context + .getBean(ConstructorParameterWithFormatProperties.class); + assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(2)); + assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(3)); + } + @Test void loadWhenBindingToConstructorParametersShouldValidate() { assertThatExceptionOfType(Exception.class) @@ -2007,6 +2056,31 @@ Period getPeriod() { } + @ConstructorBinding + @ConfigurationProperties(prefix = "test") + static class ConstructorParameterWithFormatProperties { + + private final Duration duration; + + private final Period period; + + ConstructorParameterWithFormatProperties( + @DefaultValue("2d") @DurationFormat(DurationStyle.SIMPLE) Duration duration, + @DefaultValue("3y") @PeriodFormat(PeriodStyle.SIMPLE) Period period) { + this.duration = duration; + this.period = period; + } + + Duration getDuration() { + return this.duration; + } + + Period getPeriod() { + return this.period; + } + + } + @ConstructorBinding @ConfigurationProperties(prefix = "test") @Validated @@ -2035,6 +2109,11 @@ static class ConstructorParameterWithUnitConfiguration { } + @EnableConfigurationProperties(ConstructorParameterWithFormatProperties.class) + static class ConstructorParameterWithFormatConfiguration { + + } + @EnableConfigurationProperties(ConstructorParameterValidatedProperties.class) static class ConstructorParameterValidationConfiguration {