Commit ba70b796 authored by Vlad Kisel's avatar Vlad Kisel Committed by Stephane Nicoll

Allow data unit to be specified on the constructor parameter

See gh-21746
parent 553d58a2
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -30,9 +30,10 @@ import org.springframework.util.unit.DataUnit; ...@@ -30,9 +30,10 @@ import org.springframework.util.unit.DataUnit;
* {@link DataSize}. * {@link DataSize}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Vladislav Kisel
* @since 2.1.0 * @since 2.1.0
*/ */
@Target(ElementType.FIELD) @Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
public @interface DataSizeUnit { public @interface DataSizeUnit {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -29,9 +29,10 @@ import java.time.temporal.ChronoUnit; ...@@ -29,9 +29,10 @@ import java.time.temporal.ChronoUnit;
* {@link Duration}. * {@link Duration}.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Vladislav Kisel
* @since 2.0.0 * @since 2.0.0
*/ */
@Target(ElementType.FIELD) @Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
public @interface DurationUnit { public @interface DurationUnit {
......
...@@ -30,9 +30,10 @@ import java.time.temporal.ChronoUnit; ...@@ -30,9 +30,10 @@ import java.time.temporal.ChronoUnit;
* *
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Edson Chávez * @author Edson Chávez
* @author Vladislav Kisel
* @since 2.3.0 * @since 2.3.0
*/ */
@Target(ElementType.FIELD) @Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
public @interface PeriodUnit { public @interface PeriodUnit {
......
...@@ -19,6 +19,8 @@ package org.springframework.boot.context.properties; ...@@ -19,6 +19,8 @@ package org.springframework.boot.context.properties;
import java.beans.PropertyEditorSupport; import java.beans.PropertyEditorSupport;
import java.io.File; import java.io.File;
import java.time.Duration; import java.time.Duration;
import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -54,6 +56,8 @@ import org.springframework.boot.context.properties.bind.DefaultValue; ...@@ -54,6 +56,8 @@ import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.context.properties.bind.validation.BindValidationException; import org.springframework.boot.context.properties.bind.validation.BindValidationException;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.convert.DataSizeUnit; import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.boot.convert.PeriodUnit;
import org.springframework.boot.testsupport.system.CapturedOutput; import org.springframework.boot.testsupport.system.CapturedOutput;
import org.springframework.boot.testsupport.system.OutputCaptureExtension; import org.springframework.boot.testsupport.system.OutputCaptureExtension;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
...@@ -107,6 +111,7 @@ import static org.mockito.Mockito.verify; ...@@ -107,6 +111,7 @@ import static org.mockito.Mockito.verify;
* @author Phillip Webb * @author Phillip Webb
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Madhura Bhave * @author Madhura Bhave
* @author Vladislav Kisel
*/ */
@ExtendWith(OutputCaptureExtension.class) @ExtendWith(OutputCaptureExtension.class)
class ConfigurationPropertiesTests { class ConfigurationPropertiesTests {
...@@ -129,6 +134,14 @@ class ConfigurationPropertiesTests { ...@@ -129,6 +134,14 @@ class ConfigurationPropertiesTests {
assertThat(this.context.getBean(BasicProperties.class).name).isEqualTo("foo"); assertThat(this.context.getBean(BasicProperties.class).name).isEqualTo("foo");
} }
@Test
void testConverting() {
load(BasicConfiguration.class, "name=foo");
assertThat(this.context.getBeanNamesForType(BasicProperties.class)).hasSize(1);
assertThat(this.context.containsBean(BasicProperties.class.getName())).isTrue();
assertThat(this.context.getBean(BasicProperties.class).name).isEqualTo("foo");
}
@Test @Test
void loadShouldBindNested() { void loadShouldBindNested() {
load(NestedConfiguration.class, "name=foo", "nested.name=bar"); load(NestedConfiguration.class, "name=foo", "nested.name=bar");
...@@ -762,6 +775,22 @@ class ConfigurationPropertiesTests { ...@@ -762,6 +775,22 @@ class ConfigurationPropertiesTests {
assertThat(bean.getBar()).isEqualTo(5); assertThat(bean.getBar()).isEqualTo(5);
} }
@Test
void loadWhenBindingToConstructorParametersWithConversionShouldBind() {
MutablePropertySources sources = this.context.getEnvironment().getPropertySources();
Map<String, Object> source = new HashMap<>();
source.put("test.size", "2");
source.put("test.duration", "5");
source.put("test.period", "6");
sources.addLast(new MapPropertySource("test", source));
load(ConstructorParameterWithConversionConfiguration.class);
ConstructorParameterWithConversionProperties bean = this.context
.getBean(ConstructorParameterWithConversionProperties.class);
assertThat(bean.getSize()).isEqualTo(DataSize.ofMegabytes(2));
assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(5));
assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(6));
}
@Test // gh-17831 @Test // gh-17831
void loadWhenBindingConstructorParametersViaImportShouldThrowException() { void loadWhenBindingConstructorParametersViaImportShouldThrowException() {
assertThatExceptionOfType(BeanCreationException.class) assertThatExceptionOfType(BeanCreationException.class)
...@@ -777,6 +806,16 @@ class ConfigurationPropertiesTests { ...@@ -777,6 +806,16 @@ class ConfigurationPropertiesTests {
assertThat(bean.getBar()).isEqualTo(0); assertThat(bean.getBar()).isEqualTo(0);
} }
@Test
void loadWhenBindingToConstructorParametersWithConversionAndDefaultValuesShouldBind() {
load(ConstructorParameterWithConversionConfiguration.class);
ConstructorParameterWithConversionProperties bean = this.context
.getBean(ConstructorParameterWithConversionProperties.class);
assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(4));
assertThat(bean.getSize()).isEqualTo(DataSize.ofMegabytes(3));
assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(2));
}
@Test @Test
void loadWhenBindingToConstructorParametersShouldValidate() { void loadWhenBindingToConstructorParametersShouldValidate() {
assertThatExceptionOfType(Exception.class) assertThatExceptionOfType(Exception.class)
...@@ -1933,6 +1972,39 @@ class ConfigurationPropertiesTests { ...@@ -1933,6 +1972,39 @@ class ConfigurationPropertiesTests {
} }
@ConstructorBinding
@ConfigurationProperties(prefix = "test")
@Validated
static class ConstructorParameterWithConversionProperties {
private final DataSize size;
private final Duration duration;
private final Period period;
ConstructorParameterWithConversionProperties(@DefaultValue("4") @PeriodUnit(ChronoUnit.YEARS) Period period,
@DefaultValue("3") @DataSizeUnit(DataUnit.MEGABYTES) DataSize size,
@DefaultValue("2") @DurationUnit(ChronoUnit.DAYS) Duration duration) {
this.size = size;
this.duration = duration;
this.period = period;
}
Period getPeriod() {
return this.period;
}
DataSize getSize() {
return this.size;
}
Duration getDuration() {
return this.duration;
}
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties @EnableConfigurationProperties
@Import(ConstructorParameterProperties.class) @Import(ConstructorParameterProperties.class)
...@@ -1963,6 +2035,11 @@ class ConfigurationPropertiesTests { ...@@ -1963,6 +2035,11 @@ class ConfigurationPropertiesTests {
} }
@EnableConfigurationProperties(ConstructorParameterWithConversionProperties.class)
static class ConstructorParameterWithConversionConfiguration {
}
@EnableConfigurationProperties(ConstructorParameterValidatedProperties.class) @EnableConfigurationProperties(ConstructorParameterValidatedProperties.class)
static class ConstructorParameterValidationConfiguration { static class ConstructorParameterValidationConfiguration {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment