diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java
new file mode 100644
index 0000000000..1692ea2263
--- /dev/null
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012-2017 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.autoconfigure.web.format;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.ResolverStyle;
+
+import org.joda.time.format.DateTimeFormatterBuilder;
+
+import org.springframework.format.datetime.DateFormatter;
+import org.springframework.format.datetime.DateFormatterRegistrar;
+import org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar;
+import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
+import org.springframework.format.number.NumberFormatAnnotationFormatterFactory;
+import org.springframework.format.number.money.CurrencyUnitFormatter;
+import org.springframework.format.number.money.Jsr354NumberFormatAnnotationFormatterFactory;
+import org.springframework.format.number.money.MonetaryAmountFormatter;
+import org.springframework.format.support.DefaultFormattingConversionService;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link org.springframework.format.support.FormattingConversionService} dedicated
+ * to web applications for formatting and converting values to/from the web.
+ *
+ *
This service replaces the default implementations provided by
+ * {@link org.springframework.web.servlet.config.annotation.EnableWebMvc}
+ * and {@link org.springframework.web.reactive.config.EnableWebFlux}.
+ *
+ * @author Brian Clozel
+ * @since 2.0.0
+ */
+public class WebConversionService extends DefaultFormattingConversionService {
+
+ private static final boolean jsr354Present = ClassUtils
+ .isPresent("javax.money.MonetaryAmount", WebConversionService.class.getClassLoader());
+
+ private static final boolean jodaTimePresent = ClassUtils
+ .isPresent("org.joda.time.LocalDate", WebConversionService.class.getClassLoader());
+
+ private String dateFormat;
+
+ /**
+ * Create a new WebConversionService that configures formatters with the provided date format,
+ * or register the default ones if no custom format is provided.
+ * @param dateFormat the custom date format to use for date conversions
+ */
+ public WebConversionService(String dateFormat) {
+ super(false);
+ if (StringUtils.hasText(dateFormat)) {
+ this.dateFormat = dateFormat;
+ addFormatters();
+ }
+ else {
+ addDefaultFormatters(this);
+ }
+ }
+
+
+ private void addFormatters() {
+ addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
+ if (jsr354Present) {
+ addFormatter(new CurrencyUnitFormatter());
+ addFormatter(new MonetaryAmountFormatter());
+ addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
+ }
+ registerJsr310();
+ if (jodaTimePresent) {
+ registerJodaTime();
+ }
+ registerJavaDate();
+ }
+
+ private void registerJsr310() {
+ DateTimeFormatterRegistrar dateTime = new DateTimeFormatterRegistrar();
+ if (this.dateFormat != null) {
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter
+ .ofPattern(this.dateFormat)
+ .withResolverStyle(ResolverStyle.STRICT);
+ dateTime.setDateFormatter(dateTimeFormatter);
+ }
+ dateTime.registerFormatters(this);
+ }
+
+ private void registerJodaTime() {
+ JodaTimeFormatterRegistrar jodaTime = new JodaTimeFormatterRegistrar();
+ if (this.dateFormat != null) {
+ org.joda.time.format.DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
+ .appendPattern(this.dateFormat)
+ .toFormatter();
+ jodaTime.setDateFormatter(dateTimeFormatter);
+ }
+ jodaTime.registerFormatters(this);
+ }
+
+ private void registerJavaDate() {
+ DateFormatterRegistrar dateFormatterRegistrar = new DateFormatterRegistrar();
+ if (this.dateFormat != null) {
+ DateFormatter dateFormatter = new DateFormatter(this.dateFormat);
+ dateFormatterRegistrar.setFormatter(dateFormatter);
+ }
+ dateFormatterRegistrar.registerFormatters(this);
+ }
+}
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java
index a375227d17..c173e543d4 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java
@@ -34,9 +34,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
+import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.format.WebConversionService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean;
@@ -48,6 +50,7 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
+import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.CacheControl;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.util.ClassUtils;
@@ -84,7 +87,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
@ConditionalOnClass(WebFluxConfigurer.class)
@ConditionalOnMissingBean({ WebFluxConfigurationSupport.class })
@AutoConfigureAfter({ ReactiveWebServerAutoConfiguration.class,
- CodecsAutoConfiguration.class })
+ CodecsAutoConfiguration.class, ValidationAutoConfiguration.class })
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebFluxAutoConfiguration {
@@ -211,8 +214,22 @@ public class WebFluxAutoConfiguration {
public static class EnableWebFluxConfiguration
extends DelegatingWebFluxConfiguration {
- @Override
+ private final WebFluxProperties webFluxProperties;
+
+ public EnableWebFluxConfiguration(WebFluxProperties webFluxProperties) {
+ this.webFluxProperties = webFluxProperties;
+ }
+
@Bean
+ @Override
+ public FormattingConversionService webFluxConversionService() {
+ WebConversionService conversionService = new WebConversionService(this.webFluxProperties.getDateFormat());
+ addFormatters(conversionService);
+ return conversionService;
+ }
+
+ @Bean
+ @Override
public Validator webFluxValidator() {
if (!ClassUtils.isPresent("javax.validation.Validator",
getClass().getClassLoader())) {
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java
index cfbf061acb..fe8c512fd4 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java
@@ -27,11 +27,24 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "spring.webflux")
public class WebFluxProperties {
+ /**
+ * Date format to use. For instance, "dd/MM/yyyy".
+ */
+ private String dateFormat;
+
/**
* Path pattern used for static resources.
*/
private String staticPathPattern = "/**";
+ public String getDateFormat() {
+ return this.dateFormat;
+ }
+
+ public void setDateFormat(String dateFormat) {
+ this.dateFormat = dateFormat;
+ }
+
public String getStaticPathPattern() {
return this.staticPathPattern;
}
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java
index 580446c7c8..43b130b0d9 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java
@@ -21,7 +21,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -54,6 +53,7 @@ import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
+import org.springframework.boot.autoconfigure.web.format.WebConversionService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
import org.springframework.boot.web.servlet.filter.OrderedHttpPutFormContentFilter;
@@ -73,7 +73,7 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
-import org.springframework.format.datetime.DateFormatter;
+import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
@@ -266,12 +266,6 @@ public class WebMvcAutoConfiguration {
return localeResolver;
}
- @Bean
- @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
- public Formatter dateFormatter() {
- return new DateFormatter(this.mvcProperties.getDateFormat());
- }
-
@Override
public MessageCodesResolver getMessageCodesResolver() {
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
@@ -478,6 +472,14 @@ public class WebMvcAutoConfiguration {
return super.requestMappingHandlerMapping();
}
+ @Bean
+ @Override
+ public FormattingConversionService mvcConversionService() {
+ WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
+ addFormatters(conversionService);
+ return conversionService;
+ }
+
@Bean
@Override
public Validator mvcValidator() {
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java
new file mode 100644
index 0000000000..469945aa9e
--- /dev/null
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/format/WebConversionServiceTests.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012-2017 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.autoconfigure.web.format;
+
+import java.util.Date;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link WebConversionService}.
+ *
+ * @author Brian Clozel
+ */
+public class WebConversionServiceTests {
+
+ @Test
+ public void customDateFormat() {
+ WebConversionService conversionService = new WebConversionService("dd*MM*yyyy");
+
+ Date date = new DateTime(2018, 1, 1, 20, 30).toDate();
+ assertThat(conversionService.convert(date, String.class))
+ .isEqualTo("01*01*2018");
+
+ LocalDate jodaDate = LocalDate.fromDateFields(date);
+ assertThat(conversionService.convert(jodaDate, String.class))
+ .isEqualTo("01*01*2018");
+
+ java.time.LocalDate localDate = java.time.LocalDate.of(2018, 1, 1);
+ assertThat(conversionService.convert(localDate, String.class))
+ .isEqualTo("01*01*2018");
+ }
+}
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java
index f7d3af48ab..ed51987b83 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java
@@ -16,32 +16,29 @@
package org.springframework.boot.autoconfigure.web.reactive;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Date;
import java.util.List;
import javax.validation.ValidatorFactory;
+import org.joda.time.DateTime;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
-import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfigurationTests.Config;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.test.util.TestPropertyValues;
+import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.web.codec.CodecCustomizer;
-import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.test.util.ReflectionTestUtils;
-import org.springframework.util.ObjectUtils;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.reactive.HandlerMapping;
@@ -72,240 +69,262 @@ import static org.mockito.Mockito.verify;
*/
public class WebFluxAutoConfigurationTests {
- private AnnotationConfigReactiveWebApplicationContext context;
+ private static final MockReactiveWebServerFactory mockReactiveWebServerFactory
+ = new MockReactiveWebServerFactory();
+
+ private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class))
+ .withUserConfiguration(Config.class);
@Test
public void shouldNotProcessIfExistingWebReactiveConfiguration() {
- load(WebFluxConfigurationSupport.class);
- assertThat(this.context.getBeansOfType(RequestMappingHandlerMapping.class).size())
- .isEqualTo(1);
- assertThat(this.context.getBeansOfType(RequestMappingHandlerAdapter.class).size())
- .isEqualTo(1);
+ this.contextRunner.withUserConfiguration(WebFluxConfigurationSupport.class)
+ .run(context -> {
+ assertThat(context).getBeans(RequestMappingHandlerMapping.class).hasSize(1);
+ assertThat(context).getBeans(RequestMappingHandlerAdapter.class).hasSize(1);
+ });
}
@Test
public void shouldCreateDefaultBeans() {
- load();
- assertThat(this.context.getBeansOfType(RequestMappingHandlerMapping.class).size())
- .isEqualTo(1);
- assertThat(this.context.getBeansOfType(RequestMappingHandlerAdapter.class).size())
- .isEqualTo(1);
- assertThat(this.context.getBeansOfType(RequestedContentTypeResolver.class).size())
- .isEqualTo(1);
- assertThat(this.context.getBean("resourceHandlerMapping", HandlerMapping.class))
- .isNotNull();
+ this.contextRunner.run(context -> {
+ assertThat(context).getBeans(RequestMappingHandlerMapping.class).hasSize(1);
+ assertThat(context).getBeans(RequestMappingHandlerAdapter.class).hasSize(1);
+ assertThat(context).getBeans(RequestedContentTypeResolver.class).hasSize(1);
+ assertThat(context.getBean("resourceHandlerMapping", HandlerMapping.class))
+ .isNotNull();
+ });
}
@SuppressWarnings("unchecked")
@Test
public void shouldRegisterCustomHandlerMethodArgumentResolver() {
- load(CustomArgumentResolvers.class);
- RequestMappingHandlerAdapter adapter = this.context
- .getBean(RequestMappingHandlerAdapter.class);
- List customResolvers = (List) ReflectionTestUtils
- .getField(adapter.getArgumentResolverConfigurer(), "customResolvers");
- assertThat(customResolvers).contains(
- this.context.getBean("firstResolver",
- HandlerMethodArgumentResolver.class),
- this.context.getBean("secondResolver",
- HandlerMethodArgumentResolver.class));
+ this.contextRunner.withUserConfiguration(CustomArgumentResolvers.class).run(context -> {
+ RequestMappingHandlerAdapter adapter = context
+ .getBean(RequestMappingHandlerAdapter.class);
+ List customResolvers =
+ (List) ReflectionTestUtils
+ .getField(adapter.getArgumentResolverConfigurer(), "customResolvers");
+ assertThat(customResolvers).contains(
+ context.getBean("firstResolver",
+ HandlerMethodArgumentResolver.class),
+ context.getBean("secondResolver",
+ HandlerMethodArgumentResolver.class));
+ });
}
@Test
public void shouldCustomizeCodecs() {
- load(CustomCodecCustomizers.class);
- CodecCustomizer codecCustomizer = this.context.getBean("firstCodecCustomizer",
- CodecCustomizer.class);
- assertThat(codecCustomizer).isNotNull();
- verify(codecCustomizer).customize(any(ServerCodecConfigurer.class));
+ this.contextRunner.withUserConfiguration(CustomCodecCustomizers.class).run(context -> {
+ CodecCustomizer codecCustomizer = context.getBean("firstCodecCustomizer",
+ CodecCustomizer.class);
+ assertThat(codecCustomizer).isNotNull();
+ verify(codecCustomizer).customize(any(ServerCodecConfigurer.class));
+ });
}
@Test
public void shouldRegisterResourceHandlerMapping() {
- load();
- SimpleUrlHandlerMapping hm = this.context.getBean("resourceHandlerMapping",
- SimpleUrlHandlerMapping.class);
- assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
- ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
- assertThat(staticHandler.getLocations()).hasSize(4);
- assertThat(hm.getUrlMap().get("/webjars/**"))
- .isInstanceOf(ResourceWebHandler.class);
- ResourceWebHandler webjarsHandler = (ResourceWebHandler) hm.getUrlMap()
- .get("/webjars/**");
- assertThat(webjarsHandler.getLocations()).hasSize(1);
- assertThat(webjarsHandler.getLocations().get(0))
- .isEqualTo(new ClassPathResource("/META-INF/resources/webjars/"));
+ this.contextRunner.run(context -> {
+ SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping",
+ SimpleUrlHandlerMapping.class);
+ assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
+ ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
+ assertThat(staticHandler.getLocations()).hasSize(4);
+ assertThat(hm.getUrlMap().get("/webjars/**"))
+ .isInstanceOf(ResourceWebHandler.class);
+ ResourceWebHandler webjarsHandler = (ResourceWebHandler) hm.getUrlMap()
+ .get("/webjars/**");
+ assertThat(webjarsHandler.getLocations()).hasSize(1);
+ assertThat(webjarsHandler.getLocations().get(0))
+ .isEqualTo(new ClassPathResource("/META-INF/resources/webjars/"));
+ });
}
@Test
public void shouldMapResourcesToCustomPath() {
- load("spring.webflux.static-path-pattern:/static/**");
- SimpleUrlHandlerMapping hm = this.context.getBean("resourceHandlerMapping",
- SimpleUrlHandlerMapping.class);
- assertThat(hm.getUrlMap().get("/static/**"))
- .isInstanceOf(ResourceWebHandler.class);
- ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap()
- .get("/static/**");
- assertThat(staticHandler.getLocations()).hasSize(4);
+ this.contextRunner.withPropertyValues("spring.webflux.static-path-pattern:/static/**")
+ .run(context -> {
+ SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping",
+ SimpleUrlHandlerMapping.class);
+ assertThat(hm.getUrlMap().get("/static/**"))
+ .isInstanceOf(ResourceWebHandler.class);
+ ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap()
+ .get("/static/**");
+ assertThat(staticHandler.getLocations()).hasSize(4);
+ });
}
@Test
public void shouldNotMapResourcesWhenDisabled() {
- load("spring.resources.add-mappings:false");
- assertThat(this.context.getBean("resourceHandlerMapping"))
- .isNotInstanceOf(SimpleUrlHandlerMapping.class);
+ this.contextRunner.withPropertyValues("spring.resources.add-mappings:false")
+ .run(context -> {
+ assertThat(context.getBean("resourceHandlerMapping"))
+ .isNotInstanceOf(SimpleUrlHandlerMapping.class);
+ });
}
@Test
public void resourceHandlerChainEnabled() {
- load("spring.resources.chain.enabled:true");
- SimpleUrlHandlerMapping hm = this.context.getBean("resourceHandlerMapping",
- SimpleUrlHandlerMapping.class);
- assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
- ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
- assertThat(staticHandler.getResourceResolvers()).extractingResultOf("getClass")
- .containsOnly(CachingResourceResolver.class, PathResourceResolver.class);
- assertThat(staticHandler.getResourceTransformers()).extractingResultOf("getClass")
- .containsOnly(CachingResourceTransformer.class);
+ this.contextRunner.withPropertyValues("spring.resources.chain.enabled:true")
+ .run(context -> {
+ SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping",
+ SimpleUrlHandlerMapping.class);
+ assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
+ ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
+ assertThat(staticHandler.getResourceResolvers()).extractingResultOf("getClass")
+ .containsOnly(CachingResourceResolver.class, PathResourceResolver.class);
+ assertThat(staticHandler.getResourceTransformers()).extractingResultOf("getClass")
+ .containsOnly(CachingResourceTransformer.class);
+ });
}
@Test
public void shouldRegisterViewResolvers() {
- load(ViewResolvers.class);
- ViewResolutionResultHandler resultHandler = this.context
- .getBean(ViewResolutionResultHandler.class);
- assertThat(resultHandler.getViewResolvers()).containsExactly(
- this.context.getBean("aViewResolver", ViewResolver.class),
- this.context.getBean("anotherViewResolver", ViewResolver.class));
+ this.contextRunner.withUserConfiguration(ViewResolvers.class).run(context -> {
+ ViewResolutionResultHandler resultHandler = context
+ .getBean(ViewResolutionResultHandler.class);
+ assertThat(resultHandler.getViewResolvers()).containsExactly(
+ context.getBean("aViewResolver", ViewResolver.class),
+ context.getBean("anotherViewResolver", ViewResolver.class));
+ });
+ }
+
+ @Test
+ public void noDateFormat() {
+ this.contextRunner.run((context) -> {
+ FormattingConversionService conversionService = context
+ .getBean(FormattingConversionService.class);
+ Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
+ // formatting conversion service should use simple toString()
+ assertThat(conversionService.convert(date, String.class))
+ .isEqualTo(date.toString());
+ });
+ }
+
+ @Test
+ public void overrideDateFormat() {
+ this.contextRunner.withPropertyValues("spring.webflux.date-format:dd*MM*yyyy")
+ .run((context) -> {
+ FormattingConversionService conversionService = context
+ .getBean(FormattingConversionService.class);
+ Date date = new DateTime(1988, 6, 25, 20, 30).toDate();
+ assertThat(conversionService.convert(date, String.class))
+ .isEqualTo("25*06*1988");
+ });
}
@Test
public void validatorWhenNoValidatorShouldUseDefault() {
- load(null, new Class>[] { ValidationAutoConfiguration.class });
- assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
- assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
- .isEmpty();
- String[] springValidatorBeans = this.context.getBeanNamesForType(Validator.class);
- assertThat(springValidatorBeans).containsExactly("webFluxValidator");
+ this.contextRunner
+ .run(context -> {
+ assertThat(context).doesNotHaveBean(ValidatorFactory.class);
+ assertThat(context).doesNotHaveBean(javax.validation.Validator.class);
+ assertThat(context).getBeanNames(Validator.class).containsExactly("webFluxValidator");
+ });
}
@Test
public void validatorWhenNoCustomizationShouldUseAutoConfigured() {
- load();
- String[] jsrValidatorBeans = this.context
- .getBeanNamesForType(javax.validation.Validator.class);
- String[] springValidatorBeans = this.context.getBeanNamesForType(Validator.class);
- assertThat(jsrValidatorBeans).containsExactly("defaultValidator");
- assertThat(springValidatorBeans).containsExactly("defaultValidator",
- "webFluxValidator");
- Validator validator = this.context.getBean("webFluxValidator", Validator.class);
- assertThat(validator).isInstanceOf(ValidatorAdapter.class);
- Object defaultValidator = this.context.getBean("defaultValidator");
- assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(defaultValidator);
- // Primary Spring validator is the one used by WebFlux behind the scenes
- assertThat(this.context.getBean(Validator.class)).isEqualTo(defaultValidator);
+ this.contextRunner.withConfiguration(
+ AutoConfigurations.of(ValidationAutoConfiguration.class))
+ .run(context -> {
+ assertThat(context).getBeanNames(javax.validation.Validator.class)
+ .containsExactly("defaultValidator");
+ assertThat(context).getBeanNames(Validator.class)
+ .containsExactlyInAnyOrder("defaultValidator", "webFluxValidator");
+ Validator validator = context.getBean("webFluxValidator", Validator.class);
+ assertThat(validator).isInstanceOf(ValidatorAdapter.class);
+ Object defaultValidator = context.getBean("defaultValidator");
+ assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(defaultValidator);
+ // Primary Spring validator is the one used by WebFlux behind the scenes
+ assertThat(context.getBean(Validator.class)).isEqualTo(defaultValidator);
+ });
}
@Test
public void validatorWithConfigurerShouldUseSpringValidator() {
- load(ValidatorWebFluxConfigurer.class,
- new Class>[] { ValidationAutoConfiguration.class });
- assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
- assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
- .isEmpty();
- String[] springValidatorBeans = this.context.getBeanNamesForType(Validator.class);
- assertThat(springValidatorBeans).containsExactly("webFluxValidator");
- assertThat(this.context.getBean("webFluxValidator")).isSameAs(
- this.context.getBean(ValidatorWebFluxConfigurer.class).validator);
+ this.contextRunner.withUserConfiguration(ValidatorWebFluxConfigurer.class)
+ .run(context -> {
+ assertThat(context).doesNotHaveBean(ValidatorFactory.class);
+ assertThat(context).doesNotHaveBean(javax.validation.Validator.class);
+ assertThat(context).getBeanNames(Validator.class)
+ .containsOnly("webFluxValidator");
+ assertThat(context.getBean("webFluxValidator"))
+ .isSameAs(context.getBean(ValidatorWebFluxConfigurer.class).validator);
+ });
}
@Test
public void validatorWithConfigurerDoesNotExposeJsr303() {
- load(ValidatorJsr303WebFluxConfigurer.class,
- new Class>[] { ValidationAutoConfiguration.class });
- assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
- assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
- .isEmpty();
- String[] springValidatorBeans = this.context.getBeanNamesForType(Validator.class);
- assertThat(springValidatorBeans).containsExactly("webFluxValidator");
- Validator validator = this.context.getBean("webFluxValidator", Validator.class);
- assertThat(validator).isInstanceOf(ValidatorAdapter.class);
- assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(
- this.context.getBean(ValidatorJsr303WebFluxConfigurer.class).validator);
+ this.contextRunner
+ .withUserConfiguration(ValidatorJsr303WebFluxConfigurer.class)
+ .run(context -> {
+ assertThat(context).doesNotHaveBean(ValidatorFactory.class);
+ assertThat(context).doesNotHaveBean(javax.validation.Validator.class);
+ assertThat(context).getBeanNames(Validator.class)
+ .containsOnly("webFluxValidator");
+ Validator validator = context.getBean("webFluxValidator",
+ Validator.class);
+ assertThat(validator).isInstanceOf(ValidatorAdapter.class);
+ assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(
+ context.getBean(ValidatorJsr303WebFluxConfigurer.class).validator);
+ });
}
@Test
public void validationCustomConfigurerTakesPrecedence() {
- load(ValidatorWebFluxConfigurer.class);
- assertThat(this.context.getBeansOfType(ValidatorFactory.class)).hasSize(1);
- assertThat(this.context.getBeansOfType(javax.validation.Validator.class))
- .hasSize(1);
- String[] springValidatorBeans = this.context.getBeanNamesForType(Validator.class);
- assertThat(springValidatorBeans).containsExactly("defaultValidator",
- "webFluxValidator");
- assertThat(this.context.getBean("webFluxValidator")).isSameAs(
- this.context.getBean(ValidatorWebFluxConfigurer.class).validator);
- // Primary Spring validator is the auto-configured one as the WebFlux one has been
- // customized via a WebFluxConfigurer
- assertThat(this.context.getBean(Validator.class))
- .isEqualTo(this.context.getBean("defaultValidator"));
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(ValidationAutoConfiguration.class))
+ .withUserConfiguration(ValidatorWebFluxConfigurer.class)
+ .run(context -> {
+ assertThat(context).getBeans(ValidatorFactory.class).hasSize(1);
+ assertThat(context).getBeans(javax.validation.Validator.class).hasSize(1);
+ assertThat(context).getBeanNames(Validator.class)
+ .containsExactlyInAnyOrder("defaultValidator", "webFluxValidator");
+ assertThat(context.getBean("webFluxValidator")).isSameAs(
+ context.getBean(ValidatorWebFluxConfigurer.class).validator);
+ // Primary Spring validator is the auto-configured one as the WebFlux one has been
+ // customized via a WebFluxConfigurer
+ assertThat(context.getBean(Validator.class))
+ .isEqualTo(context.getBean("defaultValidator"));
+ });
}
@Test
public void validatorWithCustomSpringValidatorIgnored() {
- load(CustomSpringValidator.class);
- String[] jsrValidatorBeans = this.context
- .getBeanNamesForType(javax.validation.Validator.class);
- String[] springValidatorBeans = this.context.getBeanNamesForType(Validator.class);
- assertThat(jsrValidatorBeans).containsExactly("defaultValidator");
- assertThat(springValidatorBeans).containsExactly("customValidator",
- "defaultValidator", "webFluxValidator");
- Validator validator = this.context.getBean("webFluxValidator", Validator.class);
- assertThat(validator).isInstanceOf(ValidatorAdapter.class);
- Object defaultValidator = this.context.getBean("defaultValidator");
- assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(defaultValidator);
- // Primary Spring validator is the one used by WebFlux behind the scenes
- assertThat(this.context.getBean(Validator.class)).isEqualTo(defaultValidator);
+ this.contextRunner
+ .withConfiguration(AutoConfigurations.of(ValidationAutoConfiguration.class))
+ .withUserConfiguration(CustomSpringValidator.class)
+ .run(context -> {
+ assertThat(context).getBeanNames(javax.validation.Validator.class)
+ .containsExactly("defaultValidator");
+ assertThat(context).getBeanNames(Validator.class)
+ .containsExactlyInAnyOrder("customValidator", "defaultValidator", "webFluxValidator");
+ Validator validator = context.getBean("webFluxValidator", Validator.class);
+ assertThat(validator).isInstanceOf(ValidatorAdapter.class);
+ Object defaultValidator = context.getBean("defaultValidator");
+ assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(defaultValidator);
+ // Primary Spring validator is the one used by WebFlux behind the scenes
+ assertThat(context.getBean(Validator.class)).isEqualTo(defaultValidator);
+ });
}
@Test
public void validatorWithCustomJsr303ValidatorExposedAsSpringValidator() {
- load(CustomJsr303Validator.class);
- assertThat(this.context.getBeansOfType(ValidatorFactory.class)).isEmpty();
- String[] jsrValidatorBeans = this.context
- .getBeanNamesForType(javax.validation.Validator.class);
- String[] springValidatorBeans = this.context.getBeanNamesForType(Validator.class);
- assertThat(jsrValidatorBeans).containsExactly("customValidator");
- assertThat(springValidatorBeans).containsExactly("webFluxValidator");
- Validator validator = this.context.getBean(Validator.class);
- assertThat(validator).isInstanceOf(ValidatorAdapter.class);
- Validator target = ((ValidatorAdapter) validator).getTarget();
- assertThat(new DirectFieldAccessor(target).getPropertyValue("targetValidator"))
- .isSameAs(this.context.getBean("customValidator"));
- }
-
- private void load(String... environment) {
- load(null, environment);
- }
-
- private void load(Class> config, String... environment) {
- load(config, null, environment);
- }
-
- private void load(Class> config, Class>[] exclude, String... environment) {
- this.context = new AnnotationConfigReactiveWebApplicationContext();
- TestPropertyValues.of(environment).applyTo(this.context);
- List> configClasses = new ArrayList<>();
- if (config != null) {
- configClasses.add(config);
- }
- configClasses.addAll(Arrays.asList(Config.class,
- ValidationAutoConfiguration.class, BaseConfiguration.class));
- if (!ObjectUtils.isEmpty(exclude)) {
- configClasses.removeAll(Arrays.asList(exclude));
- }
- this.context.register(configClasses.toArray(new Class>[configClasses.size()]));
- this.context.refresh();
+ this.contextRunner.withUserConfiguration(CustomJsr303Validator.class)
+ .run(context -> {
+ assertThat(context).doesNotHaveBean(ValidatorFactory.class);
+ assertThat(context).getBeanNames(javax.validation.Validator.class)
+ .containsExactly("customValidator");
+ assertThat(context).getBeanNames(Validator.class)
+ .containsExactly("webFluxValidator");
+ Validator validator = context.getBean(Validator.class);
+ assertThat(validator).isInstanceOf(ValidatorAdapter.class);
+ Validator target = ((ValidatorAdapter) validator).getTarget();
+ assertThat(new DirectFieldAccessor(target).getPropertyValue("targetValidator"))
+ .isSameAs(context.getBean("customValidator"));
+ });
}
@Configuration
@@ -348,13 +367,11 @@ public class WebFluxAutoConfigurationTests {
}
@Configuration
- @Import({ WebFluxAutoConfiguration.class })
- @EnableConfigurationProperties(WebFluxProperties.class)
- protected static class BaseConfiguration {
+ protected static class Config {
@Bean
public MockReactiveWebServerFactory mockReactiveWebServerFactory() {
- return new MockReactiveWebServerFactory();
+ return mockReactiveWebServerFactory;
}
}
diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
index 4fe7d392f5..94b868ea33 100644
--- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
+++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
@@ -465,7 +465,8 @@ content into your application. Rather, pick only the properties that you need.
spring.thymeleaf.template-resolver-order= # Order of the template resolver in the chain.
spring.thymeleaf.view-names= # Comma-separated list of view names that can be resolved.
- # SPRING WEB FLUX ({sc-spring-boot-autoconfigure}/web/reactive/WebFluxProperties.{sc-ext}[WebFluxProperties])
+ # SPRING WEBFLUX ({sc-spring-boot-autoconfigure}/web/reactive/WebFluxProperties.{sc-ext}[WebFluxProperties])
+ spring.webflux.date-format= # Date format to use. For instance, `dd/MM/yyyy`.
spring.webflux.static-path-pattern=/** # Path pattern used for static resources.
# SPRING WEB SERVICES ({sc-spring-boot-autoconfigure}/webservices/WebServicesProperties.{sc-ext}[WebServicesProperties])