diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerTypePredicate.java b/spring-web/src/main/java/org/springframework/web/method/HandlerTypePredicate.java index 914fea9f462..466c7f68c6a 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerTypePredicate.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerTypePredicate.java @@ -29,19 +29,24 @@ import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** - * A {@code Predicate} to match request handling component types based on the - * following selectors: + * A {@code Predicate} to match request handling component types if + * any of the following selectors match: * - *

Use static factory methods in this class to create a Predicate. + *

Composability methods on {@link Predicate} can be used : + *

+ * Predicate<Class<?>> predicate =
+ * 		HandlerTypePredicate.forAnnotation(RestController)
+ * 				.and(HandlerTypePredicate.forBasePackage("org.example"));
+ * 
* * @author Rossen Stoyanchev * @since 5.1 */ -public class HandlerTypePredicate implements Predicate> { +public class HandlerTypePredicate implements Predicate> { private final Set basePackages; diff --git a/spring-web/src/test/java/org/springframework/web/method/HandlerTypePredicateTests.java b/spring-web/src/test/java/org/springframework/web/method/HandlerTypePredicateTests.java new file mode 100644 index 00000000000..90fd29ab764 --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/method/HandlerTypePredicateTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-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.web.method; + +import java.util.function.Predicate; + +import org.junit.Test; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RestController; + +import static org.junit.Assert.*; + +/** + * Unit tests for {@link HandlerTypePredicate}. + * @author Rossen Stoyanchev + */ +public class HandlerTypePredicateTests { + + @Test + public void forAnnotation() { + + Predicate> predicate = HandlerTypePredicate.forAnnotation(Controller.class); + + assertTrue(predicate.test(HtmlController.class)); + assertTrue(predicate.test(ApiController.class)); + assertTrue(predicate.test(AnotherApiController.class)); + } + + @Test + public void forAnnotationWithException() { + + Predicate> predicate = HandlerTypePredicate.forAnnotation(Controller.class) + .and(HandlerTypePredicate.forAssignableType(Special.class)); + + assertFalse(predicate.test(HtmlController.class)); + assertFalse(predicate.test(ApiController.class)); + assertTrue(predicate.test(AnotherApiController.class)); + } + + + @Controller + private static class HtmlController {} + + @RestController + private static class ApiController {} + + @RestController + private static class AnotherApiController implements Special {} + + interface Special {} + +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java index 76eae2ba52c..bec706d1c6d 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java @@ -18,9 +18,9 @@ package org.springframework.web.reactive.config; import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Predicate; import org.springframework.lang.Nullable; -import org.springframework.web.method.HandlerTypePredicate; /** * Assist with configuring {@code HandlerMapping}'s with path matching options. @@ -39,7 +39,7 @@ public class PathMatchConfigurer { private Boolean caseSensitiveMatch; @Nullable - private Map pathPrefixes; + private Map>> pathPrefixes; /** @@ -66,13 +66,14 @@ public class PathMatchConfigurer { * Configure a path prefix to apply to matching controller methods. *

Prefixes are used to enrich the mappings of every {@code @RequestMapping} * method whose controller type is matched by the corresponding - * {@link HandlerTypePredicate}. The prefix for the first matching predicate - * is used. + * {@code Predicate}. The prefix for the first matching predicate is used. + *

Consider using {@link org.springframework.web.method.HandlerTypePredicate + * HandlerTypePredicate} to group controllers. * @param prefix the path prefix to apply * @param predicate a predicate for matching controller types * @since 5.1 */ - public PathMatchConfigurer addPathPrefix(String prefix, HandlerTypePredicate predicate) { + public PathMatchConfigurer addPathPrefix(String prefix, Predicate> predicate) { this.pathPrefixes = this.pathPrefixes == null ? new LinkedHashMap<>() : this.pathPrefixes; this.pathPrefixes.put(prefix, predicate); return this; @@ -89,7 +90,7 @@ public class PathMatchConfigurer { } @Nullable - protected Map getPathPrefixes() { + protected Map>> getPathPrefixes() { return this.pathPrefixes; } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java index 0ef94653065..8e6ae3fc381 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java @@ -18,6 +18,7 @@ package org.springframework.web.reactive.config; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import reactor.core.publisher.Mono; @@ -44,7 +45,6 @@ import org.springframework.validation.Validator; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.method.HandlerTypePredicate; import org.springframework.web.reactive.DispatcherHandler; import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; @@ -131,7 +131,7 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { mapping.setUseCaseSensitiveMatch(useCaseSensitiveMatch); } - Map pathPrefixes = configurer.getPathPrefixes(); + Map>> pathPrefixes = configurer.getPathPrefixes(); if (pathPrefixes != null) { mapping.setPathPrefixes(pathPrefixes); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java index 5cf137f1f1e..6bbed7dbaea 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Predicate; import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -34,7 +35,6 @@ import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.method.HandlerTypePredicate; import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; @@ -53,7 +53,7 @@ import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerM public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements EmbeddedValueResolverAware { - private final Map pathPrefixes = new LinkedHashMap<>(); + private final Map>> pathPrefixes = new LinkedHashMap<>(); private RequestedContentTypeResolver contentTypeResolver = new RequestedContentTypeResolverBuilder().build(); @@ -66,13 +66,16 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi /** * Configure path prefixes to apply to controller methods. *

Prefixes are used to enrich the mappings of every {@code @RequestMapping} - * method whose controller type is matched by the corresponding - * {@link HandlerTypePredicate} in the map. The prefix for the first matching - * predicate is used, assuming the input map has predictable order. + * method whose controller type is matched by a corresponding + * {@code Predicate} in the map. The prefix for the first matching predicate + * is used, assuming the input map has predictable order. + *

Consider using {@link org.springframework.web.method.HandlerTypePredicate + * HandlerTypePredicate} to group controllers. * @param prefixes a map with path prefixes as key * @since 5.1 + * @see org.springframework.web.method.HandlerTypePredicate */ - public void setPathPrefixes(Map prefixes) { + public void setPathPrefixes(Map>> prefixes) { this.pathPrefixes.clear(); prefixes.entrySet().stream() .filter(entry -> StringUtils.hasText(entry.getKey())) @@ -80,8 +83,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi } /** - * Set the {@link RequestedContentTypeResolver} to use to determine requested media types. - * If not set, the default constructor is used. + * Set the {@link RequestedContentTypeResolver} to use to determine requested + * media types. If not set, the default constructor is used. */ public void setContentTypeResolver(RequestedContentTypeResolver contentTypeResolver) { Assert.notNull(contentTypeResolver, "'contentTypeResolver' must not be null"); @@ -107,7 +110,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi * The configured path prefixes as a read-only, possibly empty map. * @since 5.1 */ - public Map getPathPrefixes() { + public Map>> getPathPrefixes() { return Collections.unmodifiableMap(this.pathPrefixes); } @@ -144,7 +147,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi if (typeInfo != null) { info = typeInfo.combine(info); } - for (Map.Entry entry : this.pathPrefixes.entrySet()) { + for (Map.Entry>> entry : this.pathPrefixes.entrySet()) { if (entry.getValue().test(handlerType)) { String prefix = entry.getKey(); if (this.embeddedValueResolver != null) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java index 6525b60c4f8..160032880d6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java @@ -18,10 +18,10 @@ package org.springframework.web.servlet.config.annotation; import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Predicate; import org.springframework.lang.Nullable; import org.springframework.util.PathMatcher; -import org.springframework.web.method.HandlerTypePredicate; import org.springframework.web.util.UrlPathHelper; /** @@ -58,7 +58,7 @@ public class PathMatchConfigurer { private PathMatcher pathMatcher; @Nullable - private Map pathPrefixes; + private Map>> pathPrefixes; /** @@ -121,13 +121,14 @@ public class PathMatchConfigurer { * Configure a path prefix to apply to matching controller methods. *

Prefixes are used to enrich the mappings of every {@code @RequestMapping} * method whose controller type is matched by the corresponding - * {@link HandlerTypePredicate}. The prefix for the first matching predicate - * is used. + * {@code Predicate}. The prefix for the first matching predicate is used. + *

Consider using {@link org.springframework.web.method.HandlerTypePredicate + * HandlerTypePredicate} to group controllers. * @param prefix the prefix to apply * @param predicate a predicate for matching controller types * @since 5.1 */ - public PathMatchConfigurer addPathPrefix(String prefix, HandlerTypePredicate predicate) { + public PathMatchConfigurer addPathPrefix(String prefix, Predicate> predicate) { this.pathPrefixes = this.pathPrefixes == null ? new LinkedHashMap<>() : this.pathPrefixes; this.pathPrefixes.put(prefix, predicate); return this; @@ -160,7 +161,7 @@ public class PathMatchConfigurer { } @Nullable - protected Map getPathPrefixes() { + protected Map>> getPathPrefixes() { return this.pathPrefixes; } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index d85b5bcc7c4..a9b5ad3ea4a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.function.Predicate; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -70,7 +71,6 @@ import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.context.ServletContextAware; import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.method.HandlerTypePredicate; import org.springframework.web.method.support.CompositeUriComponentsContributor; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; @@ -311,7 +311,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv mapping.setPathMatcher(pathMatcher); } - Map pathPrefixes = configurer.getPathPrefixes(); + Map>> pathPrefixes = configurer.getPathPrefixes(); if (pathPrefixes != null) { mapping.setPathPrefixes(pathPrefixes); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 5297bc3912e..48007022b80 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -23,6 +23,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import javax.servlet.http.HttpServletRequest; import org.springframework.context.EmbeddedValueResolverAware; @@ -38,7 +39,6 @@ import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.method.HandlerTypePredicate; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.MatchableHandlerMapping; import org.springframework.web.servlet.handler.RequestMatchResult; @@ -67,7 +67,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi private boolean useTrailingSlashMatch = true; - private final Map pathPrefixes = new LinkedHashMap<>(); + private final Map>> pathPrefixes = new LinkedHashMap<>(); private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); @@ -113,12 +113,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi * Configure path prefixes to apply to controller methods. *

Prefixes are used to enrich the mappings of every {@code @RequestMapping} * method whose controller type is matched by the corresponding - * {@link HandlerTypePredicate} in the map. The prefix for the first matching - * predicate is used, assuming the input map has predictable order. + * {@code Predicate}. The prefix for the first matching predicate is used. + *

Consider using {@link org.springframework.web.method.HandlerTypePredicate + * HandlerTypePredicate} to group controllers. * @param prefixes a map with path prefixes as key * @since 5.1 */ - public void setPathPrefixes(Map prefixes) { + public void setPathPrefixes(Map>> prefixes) { this.pathPrefixes.clear(); prefixes.entrySet().stream() .filter(entry -> StringUtils.hasText(entry.getKey())) @@ -178,7 +179,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi * The configured path prefixes as a read-only, possibly empty map. * @since 5.1 */ - public Map getPathPrefixes() { + public Map>> getPathPrefixes() { return Collections.unmodifiableMap(this.pathPrefixes); } @@ -226,7 +227,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi if (typeInfo != null) { info = typeInfo.combine(info); } - for (Map.Entry entry : this.pathPrefixes.entrySet()) { + for (Map.Entry>> entry : this.pathPrefixes.entrySet()) { if (entry.getValue().test(handlerType)) { String prefix = entry.getKey(); if (this.embeddedValueResolver != null) {