diff --git a/pom.xml b/pom.xml index 6ebe5c9eab..653adc2479 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-commons - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATACMNS-822-SNAPSHOT Spring Data Core diff --git a/src/main/asciidoc/repositories.adoc b/src/main/asciidoc/repositories.adoc index 75ec691195..edf10b3eba 100644 --- a/src/main/asciidoc/repositories.adoc +++ b/src/main/asciidoc/repositories.adoc @@ -923,7 +923,16 @@ This method signature will cause Spring MVC try to derive a Pageable instance fr |`sort`|Properties that should be sorted by in the format `property,property(,ASC\|DESC)`. Default sort direction is ascending. Use multiple `sort` parameters if you want to switch directions, e.g. `?sort=firstname&sort=lastname,asc`. |=============== -To customize this behavior extend either `SpringDataWebConfiguration` or the HATEOAS-enabled equivalent and override the `pageableResolver()` or `sortResolver()` methods and import your customized configuration file instead of using the `@Enable`-annotation. +To customize this behavior register a bean implementing the interface `PageableHandlerMethodArgumentResolverCustomizer` or `SortHandlerMethodArgumentResolverCustomizer` respectively. It's `customize()` method will get called allowing you to change settings. Like in the following example. + +[source, java] +---- +@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() { + return s -> s.setPropertyDelimiter("<-->"); +} +---- + +If setting the properties of an existing `MethodArgumentResolver` isn't sufficient for your purpose extend either `SpringDataWebConfiguration` or the HATEOAS-enabled equivalent and override the `pageableResolver()` or `sortResolver()` methods and import your customized configuration file instead of using the `@Enable`-annotation. In case you need multiple `Pageable` or `Sort` instances to be resolved from the request (for multiple tables, for example) you can use Spring's `@Qualifier` annotation to distinguish one from another. The request parameters then have to be prefixed with `${qualifier}_`. So for a method signature like this: diff --git a/src/main/java/org/springframework/data/web/config/HateoasAwareSpringDataWebConfiguration.java b/src/main/java/org/springframework/data/web/config/HateoasAwareSpringDataWebConfiguration.java index ef9351a308..069f2d6c01 100644 --- a/src/main/java/org/springframework/data/web/config/HateoasAwareSpringDataWebConfiguration.java +++ b/src/main/java/org/springframework/data/web/config/HateoasAwareSpringDataWebConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-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. @@ -36,6 +36,7 @@ * @author Oliver Gierke * @author Nick Williams * @author Ben Hale + * @author Vedran Pavic */ @Configuration public class HateoasAwareSpringDataWebConfiguration extends SpringDataWebConfiguration { @@ -56,7 +57,11 @@ public HateoasAwareSpringDataWebConfiguration(ApplicationContext context, @Bean @Override public HateoasPageableHandlerMethodArgumentResolver pageableResolver() { - return new HateoasPageableHandlerMethodArgumentResolver(sortResolver()); + + HateoasPageableHandlerMethodArgumentResolver pageableResolver = // + new HateoasPageableHandlerMethodArgumentResolver(sortResolver()); + customizePageableResolver(pageableResolver); + return pageableResolver; } /* @@ -66,7 +71,10 @@ public HateoasPageableHandlerMethodArgumentResolver pageableResolver() { @Bean @Override public HateoasSortHandlerMethodArgumentResolver sortResolver() { - return new HateoasSortHandlerMethodArgumentResolver(); + + HateoasSortHandlerMethodArgumentResolver sortResolver = new HateoasSortHandlerMethodArgumentResolver(); + customizeSortResolver(sortResolver); + return sortResolver; } @Bean diff --git a/src/main/java/org/springframework/data/web/config/PageableHandlerMethodArgumentResolverCustomizer.java b/src/main/java/org/springframework/data/web/config/PageableHandlerMethodArgumentResolverCustomizer.java new file mode 100644 index 0000000000..6b8220e9ce --- /dev/null +++ b/src/main/java/org/springframework/data/web/config/PageableHandlerMethodArgumentResolverCustomizer.java @@ -0,0 +1,35 @@ +/* + * Copyright 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.data.web.config; + +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link PageableHandlerMethodArgumentResolver} configuration. + * + * @author Vedran Pavic + */ +public interface PageableHandlerMethodArgumentResolverCustomizer { + + /** + * Customize the pageable resolver + * + * @param pageableResolver the {@link PageableHandlerMethodArgumentResolver} to customize + */ + void customize(PageableHandlerMethodArgumentResolver pageableResolver); + +} diff --git a/src/main/java/org/springframework/data/web/config/SortHandlerMethodArgumentResolverCustomizer.java b/src/main/java/org/springframework/data/web/config/SortHandlerMethodArgumentResolverCustomizer.java new file mode 100644 index 0000000000..67bf79985d --- /dev/null +++ b/src/main/java/org/springframework/data/web/config/SortHandlerMethodArgumentResolverCustomizer.java @@ -0,0 +1,35 @@ +/* + * Copyright 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.data.web.config; + +import org.springframework.data.web.SortHandlerMethodArgumentResolver; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link SortHandlerMethodArgumentResolver} configuration. + * + * @author Vedran Pavic + */ +public interface SortHandlerMethodArgumentResolverCustomizer { + + /** + * Customize the sort resolver + * + * @param sortResolver the {@link SortHandlerMethodArgumentResolver} to customize + */ + void customize(SortHandlerMethodArgumentResolver sortResolver); + +} diff --git a/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java b/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java index 93e6190dc6..76b4af18e0 100644 --- a/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java +++ b/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java @@ -16,8 +16,10 @@ package org.springframework.data.web.config; import java.util.List; +import java.util.Optional; import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -46,6 +48,7 @@ * * @since 1.6 * @author Oliver Gierke + * @author Vedran Pavic * @author Jens Schauder */ @Configuration @@ -54,8 +57,13 @@ public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter { private final ApplicationContext context; private final ObjectFactory conversionService; - public SpringDataWebConfiguration(ApplicationContext context, - @Qualifier("mvcConversionService") ObjectFactory conversionService) { + @Autowired private Optional pageableResolverCustomizer; + @Autowired private Optional sortResolverCustomizer; + + public SpringDataWebConfiguration( // + ApplicationContext context, // + @Qualifier("mvcConversionService") ObjectFactory conversionService // + ) { this.context = context; this.conversionService = conversionService; @@ -67,7 +75,11 @@ public SpringDataWebConfiguration(ApplicationContext context, */ @Bean public PageableHandlerMethodArgumentResolver pageableResolver() { - return new PageableHandlerMethodArgumentResolver(sortResolver()); + + PageableHandlerMethodArgumentResolver pageableResolver = // + new PageableHandlerMethodArgumentResolver(sortResolver()); + customizePageableResolver(pageableResolver); + return pageableResolver; } /* @@ -76,7 +88,10 @@ public PageableHandlerMethodArgumentResolver pageableResolver() { */ @Bean public SortHandlerMethodArgumentResolver sortResolver() { - return new SortHandlerMethodArgumentResolver(); + + SortHandlerMethodArgumentResolver sortResolver = new SortHandlerMethodArgumentResolver(); + customizeSortResolver(sortResolver); + return sortResolver; } /* @@ -95,8 +110,7 @@ public void addFormatters(FormatterRegistry registry) { FormattingConversionService conversionService = (FormattingConversionService) registry; - DomainClassConverter converter = new DomainClassConverter<>( - conversionService); + DomainClassConverter converter = new DomainClassConverter<>(conversionService); converter.setApplicationContext(context); } @@ -139,4 +153,13 @@ public void extendMessageConverters(List> converters) { converters.add(0, new XmlBeamHttpMessageConverter()); } } + + protected void customizePageableResolver(PageableHandlerMethodArgumentResolver pageableResolver) { + pageableResolverCustomizer.ifPresent(c -> c.customize(pageableResolver)); + } + + protected void customizeSortResolver(SortHandlerMethodArgumentResolver sortResolver) { + sortResolverCustomizer.ifPresent(c -> c.customize(sortResolver)); + } + } diff --git a/src/test/java/org/springframework/data/web/config/EnableSpringDataWebSupportIntegrationTests.java b/src/test/java/org/springframework/data/web/config/EnableSpringDataWebSupportIntegrationTests.java index e78e736cb9..5ebc6382b4 100755 --- a/src/test/java/org/springframework/data/web/config/EnableSpringDataWebSupportIntegrationTests.java +++ b/src/test/java/org/springframework/data/web/config/EnableSpringDataWebSupportIntegrationTests.java @@ -36,6 +36,7 @@ import org.springframework.data.web.SortHandlerMethodArgumentResolver; import org.springframework.data.web.WebTestUtils; import org.springframework.data.web.config.EnableSpringDataWebSupport.SpringDataWebConfigurationImportSelector; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.util.ReflectionUtils; @@ -49,6 +50,8 @@ * Integration tests for {@link EnableSpringDataWebSupport}. * * @author Oliver Gierke + * @author Vedran Pavic + * @author Jens Schauder */ public class EnableSpringDataWebSupportIntegrationTests { @@ -65,8 +68,31 @@ static class SampleConfig { } } + @Configuration + @EnableWebMvc + @EnableSpringDataWebSupport + static class PageableResolverCustomizerConfig extends SampleConfig { + + @Bean + public PageableHandlerMethodArgumentResolverCustomizer testPageableResolverCustomizer() { + return pageableResolver -> pageableResolver.setMaxPageSize(100); + } + } + + @Configuration + @EnableWebMvc + @EnableSpringDataWebSupport + static class SortResolverCustomizerConfig extends SampleConfig { + + @Bean + public SortHandlerMethodArgumentResolverCustomizer testSortResolverCustomizer() { + return sortResolver -> sortResolver.setSortParameter("foo"); + } + } + @After public void tearDown() { + reEnable(HATEOAS); reEnable(JACKSON); } @@ -165,6 +191,28 @@ public void picksUpWebConfigurationMixins() { assertThat(names).contains("sampleBean"); } + @Test // DATACMNS-822 + public void picksUpPageableResolverCustomizer() { + + ApplicationContext context = WebTestUtils.createApplicationContext(PageableResolverCustomizerConfig.class); + List names = Arrays.asList(context.getBeanDefinitionNames()); + PageableHandlerMethodArgumentResolver resolver = context.getBean(PageableHandlerMethodArgumentResolver.class); + + assertThat(names).contains("testPageableResolverCustomizer"); + assertThat((Integer) ReflectionTestUtils.getField(resolver, "maxPageSize")).isEqualTo(100); + } + + @Test // DATACMNS-822 + public void picksUpSortResolverCustomizer() { + + ApplicationContext context = WebTestUtils.createApplicationContext(SortResolverCustomizerConfig.class); + List names = Arrays.asList(context.getBeanDefinitionNames()); + SortHandlerMethodArgumentResolver resolver = context.getBean(SortHandlerMethodArgumentResolver.class); + + assertThat(names).contains("testSortResolverCustomizer"); + assertThat((String) ReflectionTestUtils.getField(resolver, "sortParameter")).isEqualTo("foo"); + } + private static void assertResolversRegistered(ApplicationContext context, Class... resolverTypes) { RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);