diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc index 83bd87d1cf..56c684410a 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/index.adoc @@ -36,6 +36,7 @@ Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; :dc-spring-boot-test-autoconfigure: {dc-root}/org/springframework/boot/test/autoconfigure :dependency-management-plugin: https://github.com/spring-gradle-plugins/dependency-management-plugin :dependency-management-plugin-documentation: {dependency-management-plugin}/blob/master/README.md +:java-javadoc: https://docs.oracle.com/javase/8/docs/api/ :spring-boot-actuator-api: https://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/actuator-api/ :spring-boot-maven-plugin-site: https://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/maven-plugin :spring-boot-gradle-plugin: https://docs.spring.io/spring-boot/docs/{spring-boot-docs-version}/gradle-plugin diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 7e541b531d..dc2b0c9c61 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1177,8 +1177,8 @@ such as `my.property-name=acme`. [[boot-features-external-config-conversion]] ==== Properties Conversion -Spring attempts to coerce the external application properties to the right type when it -binds to the `@ConfigurationProperties` beans. If you need custom type conversion, you +Spring Boot attempts to coerce the external application properties to the right type when +it binds to the `@ConfigurationProperties` beans. If you need custom type conversion, you can provide a `ConversionService` bean (with a bean named `conversionService`) or custom property editors (through a `CustomEditorConfigurer` bean) or custom `Converters` (with bean definitions annotated as `@ConfigurationPropertiesBinding`). @@ -1191,6 +1191,50 @@ only rely on custom converters qualified with `@ConfigurationPropertiesBinding`. +[[boot-features-external-config-conversion-duration]] +===== Converting durations +Spring Boot has dedicated support for expressing durations. If you expose a +`java.time.Duration` property, the following formats in application properties are +available: + +* A regular `long` representation (using milliseconds as the default unit unless a +`@DefaultUnit` has been specified) +* The standard ISO-8601 format +{java-javadoc}/java/time/Duration.html#parse-java.lang.CharSequence-[used by +`java.util.Duration`] +* A more readable format where the value and the unit are coupled (e.g. `10s` means 10 +seconds) + +Consider the following example: + +[source,java,indent=0] +---- +include::{code-examples}/context/properties/bind/AppSystemProperties.java[tag=example] +---- + +To specify a session timeout of 30 seconds, `30`, `PT30S` and `30s` are all equivalent. A +read timeout of 500ms can be specified in any of the following form: `500`, `PT0.5S` and +`500ms`. + +You can also use any of the supported unit. These are: + +* `ns` for nanoseconds +* `ms` for milliseconds +* `s` for seconds +* `m` for minutes +* `h` for hours +* `d` for days + +The default unit is milliseconds and can be overridden using `@DefaultUnit` as illustrated +in the sample above. + +TIP: If you are upgrading from a previous version that is simply using `Long` to express +the duration, make sure to define the unit (using `@DefaultUnit`) if it isn't +milliseconds alongside the switch to `Duration`. Doing so gives a transparent upgrade path +while supporting a much richer format. + + + [[boot-features-external-config-validation]] ==== @ConfigurationProperties Validation Spring Boot attempts to validate `@ConfigurationProperties` classes whenever they are @@ -1387,7 +1431,7 @@ See "<>" for details. Spring Boot uses http://commons.apache.org/logging[Commons Logging] for all internal logging but leaves the underlying log implementation open. Default configurations are provided for -https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html[Java Util +{java-javadoc}/java/util/logging/package-summary.html[Java Util Logging], http://logging.apache.org/log4j/2.x/[Log4J2], and http://logback.qos.ch/[Logback]. In each case, loggers are pre-configured to use console output with optional file output also available. diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/properties/bind/AppSystemProperties.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/properties/bind/AppSystemProperties.java new file mode 100644 index 0000000000..37584309e9 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/properties/bind/AppSystemProperties.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2018 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 + * + * http://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; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.convert.DurationUnit; + +/** + * A {@link ConfigurationProperties} example that uses {@link Duration}. + * + * @author Stephane Nicoll + */ +// tag::example[] +@ConfigurationProperties("app.system") +public class AppSystemProperties { + + @DurationUnit(ChronoUnit.SECONDS) + private Duration sessionTimeout = Duration.ofSeconds(30); + + private Duration readTimeout = Duration.ofMillis(1000); + + public Duration getSessionTimeout() { + return this.sessionTimeout; + } + + public void setSessionTimeout(Duration sessionTimeout) { + this.sessionTimeout = sessionTimeout; + } + + public Duration getReadTimeout() { + return this.readTimeout; + } + + public void setReadTimeout(Duration readTimeout) { + this.readTimeout = readTimeout; + } + +} +// end::example[] diff --git a/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/context/properties/bind/AppSystemPropertiesTests.java b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/context/properties/bind/AppSystemPropertiesTests.java new file mode 100644 index 0000000000..76730e3998 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/test/java/org/springframework/boot/docs/context/properties/bind/AppSystemPropertiesTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012-2018 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 + * + * http://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; + +import java.time.Duration; +import java.util.function.Consumer; + +import org.junit.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 + */ +public class AppSystemPropertiesTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withUserConfiguration(Config.class); + + @Test + public void bindWithDefaultUnit() { + this.contextRunner.withPropertyValues("app.system.session-timeout=40", + "app.system.read-timeout=5000").run(assertBinding(p -> { + assertThat(p.getSessionTimeout()).isEqualTo(Duration.ofSeconds(40)); + assertThat(p.getReadTimeout()).isEqualTo(Duration.ofMillis(5000)); + })); + } + + @Test + public void bindWithExplicitUnit() { + this.contextRunner.withPropertyValues("app.system.session-timeout=1h", + "app.system.read-timeout=5s").run(assertBinding(p -> { + assertThat(p.getSessionTimeout()).isEqualTo(Duration.ofMinutes(60)); + assertThat(p.getReadTimeout()).isEqualTo(Duration.ofMillis(5000)); + })); + } + + @Test + public void bindWithIso8601Format() { + this.contextRunner.withPropertyValues("app.system.session-timeout=PT15S", + "app.system.read-timeout=PT0.5S").run(assertBinding(p -> { + assertThat(p.getSessionTimeout()).isEqualTo(Duration.ofSeconds(15)); + assertThat(p.getReadTimeout()).isEqualTo(Duration.ofMillis(500)); + })); + } + + private ContextConsumer assertBinding( + Consumer appSystemProperties) { + return (context) -> { + assertThat(context).hasSingleBean(AppSystemProperties.class); + appSystemProperties.accept(context.getBean(AppSystemProperties.class)); + }; + } + + @Configuration + @EnableConfigurationProperties(AppSystemProperties.class) + static class Config { + + } + +}