Commit 429a504b authored by Stephane Nicoll's avatar Stephane Nicoll

Polish "Allow data unit to be specified on the constructor parameter"

See gh-21746
parent ba70b796
...@@ -1426,7 +1426,7 @@ Consider the following example: ...@@ -1426,7 +1426,7 @@ Consider the following example:
[source,java,indent=0] [source,java,indent=0]
---- ----
include::{code-examples}/context/properties/bind/AppSystemProperties.java[tag=example] include::{code-examples}/context/properties/bind/javabean/AppSystemProperties.java[tag=example]
---- ----
To specify a session timeout of 30 seconds, `30`, `PT30S` and `30s` are all equivalent. To specify a session timeout of 30 seconds, `30`, `PT30S` and `30s` are all equivalent.
...@@ -1445,6 +1445,14 @@ These are: ...@@ -1445,6 +1445,14 @@ These are:
The default unit is milliseconds and can be overridden using `@DurationUnit` as illustrated in the sample above. The default unit is milliseconds and can be overridden using `@DurationUnit` as illustrated in the sample above.
If you prefer to use constructor binding, the same properties can be exposed, as shown in the following example:
[source,java,indent=0]
----
include::{code-examples}/context/properties/bind/constructor/AppSystemProperties.java[tag=example]
----
TIP: If you are upgrading a `Long` property, make sure to define the unit (using `@DurationUnit`) if it isn't milliseconds. TIP: If you are upgrading a `Long` property, make sure to define the unit (using `@DurationUnit`) if it isn't milliseconds.
Doing so gives a transparent upgrade path while supporting a much richer format. Doing so gives a transparent upgrade path while supporting a much richer format.
...@@ -1482,7 +1490,7 @@ Consider the following example: ...@@ -1482,7 +1490,7 @@ Consider the following example:
[source,java,indent=0] [source,java,indent=0]
---- ----
include::{code-examples}/context/properties/bind/AppIoProperties.java[tag=example] include::{code-examples}/context/properties/bind/javabean/AppIoProperties.java[tag=example]
---- ----
To specify a buffer size of 10 megabytes, `10` and `10MB` are equivalent. To specify a buffer size of 10 megabytes, `10` and `10MB` are equivalent.
...@@ -1499,6 +1507,13 @@ These are: ...@@ -1499,6 +1507,13 @@ These are:
The default unit is bytes and can be overridden using `@DataSizeUnit` as illustrated in the sample above. The default unit is bytes and can be overridden using `@DataSizeUnit` as illustrated in the sample above.
If you prefer to use constructor binding, the same properties can be exposed, as shown in the following example:
[source,java,indent=0]
----
include::{code-examples}/context/properties/bind/constructor/AppIoProperties.java[tag=example]
----
TIP: If you are upgrading a `Long` property, make sure to define the unit (using `@DataSizeUnit`) if it isn't bytes. TIP: If you are upgrading a `Long` property, make sure to define the unit (using `@DataSizeUnit`) if it isn't bytes.
Doing so gives a transparent upgrade path while supporting a much richer format. Doing so gives a transparent upgrade path while supporting a much richer format.
......
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.docs.context.properties.bind.constructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
/**
* A {@link ConfigurationProperties @ConfigurationProperties} example that uses
* {@link DataSize}.
*
* @author Stephane Nicoll
*/
// tag::example[]
@ConfigurationProperties("app.io")
@ConstructorBinding
public class AppIoProperties {
private final DataSize bufferSize;
private final DataSize sizeThreshold;
public AppIoProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize,
@DefaultValue("512B") DataSize sizeThreshold) {
this.bufferSize = bufferSize;
this.sizeThreshold = sizeThreshold;
}
public DataSize getBufferSize() {
return this.bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
}
// end::example[]
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.docs.context.properties.bind.constructor;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DurationUnit;
/**
* A {@link ConfigurationProperties @ConfigurationProperties} example that uses
* {@link Duration}.
*
* @author Stephane Nicoll
*/
// tag::example[]
@ConfigurationProperties("app.system")
@ConstructorBinding
public class AppSystemProperties {
private final Duration sessionTimeout;
private final Duration readTimeout;
public AppSystemProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout,
@DefaultValue("1000ms") Duration readTimeout) {
this.sessionTimeout = sessionTimeout;
this.readTimeout = readTimeout;
}
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
}
// end::example[]
/* /*
* 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.
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.docs.context.properties.bind; package org.springframework.boot.docs.context.properties.bind.javabean;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit; import org.springframework.boot.convert.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.
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.docs.context.properties.bind; package org.springframework.boot.docs.context.properties.bind.javabean;
import java.time.Duration; import java.time.Duration;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
......
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.docs.context.properties.bind.constructor;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link AppSystemProperties}.
*
* @author Stephane Nicoll
*/
class AppSystemPropertiesTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(Config.class);
@Test
void bindWithDefaultUnit() {
this.contextRunner.withPropertyValues("app.system.session-timeout=40", "app.system.read-timeout=5000")
.run(assertBinding((properties) -> {
assertThat(properties.getSessionTimeout()).hasSeconds(40);
assertThat(properties.getReadTimeout()).hasMillis(5000);
}));
}
@Test
void bindWithExplicitUnit() {
this.contextRunner.withPropertyValues("app.system.session-timeout=1h", "app.system.read-timeout=5s")
.run(assertBinding((properties) -> {
assertThat(properties.getSessionTimeout()).hasMinutes(60);
assertThat(properties.getReadTimeout()).hasMillis(5000);
}));
}
@Test
void bindWithIso8601Format() {
this.contextRunner.withPropertyValues("app.system.session-timeout=PT15S", "app.system.read-timeout=PT0.5S")
.run(assertBinding((properties) -> {
assertThat(properties.getSessionTimeout()).hasSeconds(15);
assertThat(properties.getReadTimeout()).hasMillis(500);
}));
}
private ContextConsumer<AssertableApplicationContext> assertBinding(Consumer<AppSystemProperties> properties) {
return (context) -> {
assertThat(context).hasSingleBean(AppSystemProperties.class);
properties.accept(context.getBean(AppSystemProperties.class));
};
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AppSystemProperties.class)
static class Config {
}
}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.docs.context.properties.bind; package org.springframework.boot.docs.context.properties.bind.javabean;
import java.util.function.Consumer; import java.util.function.Consumer;
......
...@@ -30,7 +30,6 @@ import org.springframework.util.unit.DataUnit; ...@@ -30,7 +30,6 @@ 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, ElementType.PARAMETER }) @Target({ ElementType.FIELD, ElementType.PARAMETER })
......
...@@ -29,7 +29,6 @@ import java.time.temporal.ChronoUnit; ...@@ -29,7 +29,6 @@ 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, ElementType.PARAMETER }) @Target({ ElementType.FIELD, ElementType.PARAMETER })
......
...@@ -30,7 +30,6 @@ import java.time.temporal.ChronoUnit; ...@@ -30,7 +30,6 @@ 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, ElementType.PARAMETER }) @Target({ ElementType.FIELD, ElementType.PARAMETER })
......
...@@ -134,14 +134,6 @@ class ConfigurationPropertiesTests { ...@@ -134,14 +134,6 @@ 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");
...@@ -776,19 +768,19 @@ class ConfigurationPropertiesTests { ...@@ -776,19 +768,19 @@ class ConfigurationPropertiesTests {
} }
@Test @Test
void loadWhenBindingToConstructorParametersWithConversionShouldBind() { void loadWhenBindingToConstructorParametersWithCustomDataUnitShouldBind() {
MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); MutablePropertySources sources = this.context.getEnvironment().getPropertySources();
Map<String, Object> source = new HashMap<>(); Map<String, Object> source = new HashMap<>();
source.put("test.size", "2"); source.put("test.duration", "12");
source.put("test.duration", "5"); source.put("test.size", "13");
source.put("test.period", "6"); source.put("test.period", "14");
sources.addLast(new MapPropertySource("test", source)); sources.addLast(new MapPropertySource("test", source));
load(ConstructorParameterWithConversionConfiguration.class); load(ConstructorParameterWithUnitConfiguration.class);
ConstructorParameterWithConversionProperties bean = this.context ConstructorParameterWithUnitProperties bean = this.context
.getBean(ConstructorParameterWithConversionProperties.class); .getBean(ConstructorParameterWithUnitProperties.class);
assertThat(bean.getSize()).isEqualTo(DataSize.ofMegabytes(2)); assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(12));
assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(5)); assertThat(bean.getSize()).isEqualTo(DataSize.ofMegabytes(13));
assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(6)); assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(14));
} }
@Test // gh-17831 @Test // gh-17831
...@@ -807,13 +799,13 @@ class ConfigurationPropertiesTests { ...@@ -807,13 +799,13 @@ class ConfigurationPropertiesTests {
} }
@Test @Test
void loadWhenBindingToConstructorParametersWithConversionAndDefaultValuesShouldBind() { void loadWhenBindingToConstructorParametersWithDefaultDataUnitShouldBind() {
load(ConstructorParameterWithConversionConfiguration.class); load(ConstructorParameterWithUnitConfiguration.class);
ConstructorParameterWithConversionProperties bean = this.context ConstructorParameterWithUnitProperties bean = this.context
.getBean(ConstructorParameterWithConversionProperties.class); .getBean(ConstructorParameterWithUnitProperties.class);
assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(4));
assertThat(bean.getSize()).isEqualTo(DataSize.ofMegabytes(3));
assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(2)); assertThat(bean.getDuration()).isEqualTo(Duration.ofDays(2));
assertThat(bean.getSize()).isEqualTo(DataSize.ofMegabytes(3));
assertThat(bean.getPeriod()).isEqualTo(Period.ofYears(4));
} }
@Test @Test
...@@ -1974,33 +1966,32 @@ class ConfigurationPropertiesTests { ...@@ -1974,33 +1966,32 @@ class ConfigurationPropertiesTests {
@ConstructorBinding @ConstructorBinding
@ConfigurationProperties(prefix = "test") @ConfigurationProperties(prefix = "test")
@Validated static class ConstructorParameterWithUnitProperties {
static class ConstructorParameterWithConversionProperties {
private final DataSize size;
private final Duration duration; private final Duration duration;
private final DataSize size;
private final Period period; private final Period period;
ConstructorParameterWithConversionProperties(@DefaultValue("4") @PeriodUnit(ChronoUnit.YEARS) Period period, ConstructorParameterWithUnitProperties(@DefaultValue("2") @DurationUnit(ChronoUnit.DAYS) Duration duration,
@DefaultValue("3") @DataSizeUnit(DataUnit.MEGABYTES) DataSize size, @DefaultValue("3") @DataSizeUnit(DataUnit.MEGABYTES) DataSize size,
@DefaultValue("2") @DurationUnit(ChronoUnit.DAYS) Duration duration) { @DefaultValue("4") @PeriodUnit(ChronoUnit.YEARS) Period period) {
this.size = size; this.size = size;
this.duration = duration; this.duration = duration;
this.period = period; this.period = period;
} }
Period getPeriod() { Duration getDuration() {
return this.period; return this.duration;
} }
DataSize getSize() { DataSize getSize() {
return this.size; return this.size;
} }
Duration getDuration() { Period getPeriod() {
return this.duration; return this.period;
} }
} }
...@@ -2035,8 +2026,8 @@ class ConfigurationPropertiesTests { ...@@ -2035,8 +2026,8 @@ class ConfigurationPropertiesTests {
} }
@EnableConfigurationProperties(ConstructorParameterWithConversionProperties.class) @EnableConfigurationProperties(ConstructorParameterWithUnitProperties.class)
static class ConstructorParameterWithConversionConfiguration { static class ConstructorParameterWithUnitConfiguration {
} }
......
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