diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ConsumesRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ConsumesRequestCondition.java index cca903941b..671780e621 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ConsumesRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ConsumesRequestCondition.java @@ -25,6 +25,8 @@ import java.util.Set; import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; +import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.cors.reactive.CorsUtils; import org.springframework.web.server.ServerWebExchange; @@ -68,15 +70,16 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition(parseExpressions(consumes, headers)); + Collections.sort(this.expressions); } /** - * Private constructor accepting parsed media type expressions. + * Private constructor for internal when creating matching conditions. + * Note the expressions List is neither sorted nor deep copied. */ - private ConsumesRequestCondition(Collection expressions) { - this.expressions = new ArrayList<>(expressions); - Collections.sort(this.expressions); + private ConsumesRequestCondition(List expressions) { + this.expressions = expressions; } @@ -166,9 +169,20 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition result = new LinkedHashSet<>(this.expressions); - result.removeIf(expression -> !expression.match(exchange)); - return (!result.isEmpty() ? new ConsumesRequestCondition(result) : null); + List result = getMatchingExpressions(exchange); + return !CollectionUtils.isEmpty(result) ? new ConsumesRequestCondition(result) : null; + } + + @Nullable + private List getMatchingExpressions(ServerWebExchange exchange) { + List result = null; + for (ConsumeMediaTypeExpression expression : this.expressions) { + if (expression.match(exchange)) { + result = result != null ? result : new ArrayList<>(); + result.add(expression); + } + } + return result; } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/HeadersRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/HeadersRequestCondition.java index 9136c6d9a1..878a70eb90 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/HeadersRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/HeadersRequestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -17,7 +17,6 @@ package org.springframework.web.reactive.result.condition; import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -56,12 +55,12 @@ public final class HeadersRequestCondition extends AbstractRequestCondition conditions) { - this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions)); + private HeadersRequestCondition(Set conditions) { + this.expressions = conditions; } - private static Collection parseExpressions(String... headers) { + private static Set parseExpressions(String... headers) { Set expressions = new LinkedHashSet<>(); if (headers != null) { for (String header : headers) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java index 75c09038f6..ff87244c8b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ProducesRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ProducesRequestCondition.java index a90597c372..8ed2942210 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ProducesRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ProducesRequestCondition.java @@ -17,7 +17,6 @@ package org.springframework.web.reactive.result.condition; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -25,11 +24,11 @@ import java.util.Set; import org.springframework.http.MediaType; import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.cors.reactive.CorsUtils; -import org.springframework.web.reactive.accept.HeaderContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.server.NotAcceptableStatusException; @@ -48,6 +47,9 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException; */ public final class ProducesRequestCondition extends AbstractRequestCondition { + private static final RequestedContentTypeResolver DEFAULT_CONTENT_TYPE_RESOLVER = + new RequestedContentTypeResolverBuilder().build(); + private static final ProducesRequestCondition EMPTY_CONDITION = new ProducesRequestCondition(); @@ -90,18 +92,16 @@ public final class ProducesRequestCondition extends AbstractRequestCondition(parseExpressions(produces, headers)); Collections.sort(this.expressions); - this.contentTypeResolver = (resolver != null ? resolver : new HeaderContentTypeResolver()); + this.contentTypeResolver = resolver != null ? resolver : DEFAULT_CONTENT_TYPE_RESOLVER; } /** - * Private constructor with already parsed media type expressions. + * Private constructor for internal use to create matching conditions. + * Note the expressions List is neither sorted, nor deep copied. */ - private ProducesRequestCondition(Collection expressions, - RequestedContentTypeResolver resolver) { - - this.expressions = new ArrayList<>(expressions); - Collections.sort(this.expressions); - this.contentTypeResolver = (resolver != null ? resolver : new RequestedContentTypeResolverBuilder().build()); + private ProducesRequestCondition(List expressions, ProducesRequestCondition other) { + this.expressions = expressions; + this.contentTypeResolver = other.contentTypeResolver; } @@ -189,10 +189,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition result = new LinkedHashSet<>(this.expressions); - result.removeIf(expression -> !expression.match(exchange)); - if (!result.isEmpty()) { - return new ProducesRequestCondition(result, this.contentTypeResolver); + List result = getMatchingExpressions(exchange); + if (!CollectionUtils.isEmpty(result)) { + return new ProducesRequestCondition(result, this); } else { try { @@ -207,6 +206,18 @@ public final class ProducesRequestCondition extends AbstractRequestCondition getMatchingExpressions(ServerWebExchange exchange) { + List result = null; + for (ProduceMediaTypeExpression expression : this.expressions) { + if (expression.match(exchange)) { + result = result != null ? result : new ArrayList<>(); + result.add(expression); + } + } + return result; + } + /** * Compares this and another "produces" condition as follows: *
    diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java index cda3f8bd39..636347061a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java @@ -19,8 +19,10 @@ package org.springframework.web.reactive.result.condition; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.springframework.http.HttpMethod; @@ -39,8 +41,15 @@ import org.springframework.web.server.ServerWebExchange; */ public final class RequestMethodsRequestCondition extends AbstractRequestCondition { - private static final RequestMethodsRequestCondition GET_CONDITION = - new RequestMethodsRequestCondition(RequestMethod.GET); + /** Per HTTP method cache to return ready instances from getMatchingCondition. */ + private static final Map requestMethodConditionCache; + + static { + requestMethodConditionCache = new HashMap<>(RequestMethod.values().length); + for (RequestMethod method : RequestMethod.values()) { + requestMethodConditionCache.put(method.name(), new RequestMethodsRequestCondition(method)); + } + } private final Set methods; @@ -110,11 +119,11 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi } if (getMethods().isEmpty()) { if (RequestMethod.OPTIONS.name().equals(exchange.getRequest().getMethodValue())) { - return null; // No implicit match for OPTIONS (we handle it) + return null; // We handle OPTIONS transparently, so don't match if no explicit declarations } return this; } - return matchRequestMethod(exchange.getRequest().getMethod()); + return matchRequestMethod(exchange.getRequest().getMethodValue()); } /** @@ -122,24 +131,25 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi * Hence empty conditions is a match, otherwise try to match to the HTTP * method in the "Access-Control-Request-Method" header. */ + @Nullable private RequestMethodsRequestCondition matchPreFlight(ServerHttpRequest request) { if (getMethods().isEmpty()) { return this; } HttpMethod expectedMethod = request.getHeaders().getAccessControlRequestMethod(); - return matchRequestMethod(expectedMethod); + return expectedMethod != null ? matchRequestMethod(expectedMethod.name()) : null; } @Nullable - private RequestMethodsRequestCondition matchRequestMethod(@Nullable HttpMethod httpMethod) { + private RequestMethodsRequestCondition matchRequestMethod(@Nullable String httpMethod) { if (httpMethod != null) { for (RequestMethod method : getMethods()) { if (httpMethod.matches(method.name())) { - return new RequestMethodsRequestCondition(method); + return requestMethodConditionCache.get(method.name()); } } - if (httpMethod == HttpMethod.HEAD && getMethods().contains(RequestMethod.GET)) { - return GET_CONDITION; + if (HttpMethod.HEAD.matches(httpMethod) && getMethods().contains(RequestMethod.GET)) { + return requestMethodConditionCache.get(HttpMethod.GET.name()); } } return null; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/AbstractNameValueExpression.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/AbstractNameValueExpression.java index 38e6f42b7d..5acc07c3dc 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/AbstractNameValueExpression.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/AbstractNameValueExpression.java @@ -80,7 +80,7 @@ abstract class AbstractNameValueExpression implements NameValueExpression else { isMatch = matchName(request); } - return (this.isNegated ? !isMatch : isMatch); + return this.isNegated != isMatch; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ConsumesRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ConsumesRequestCondition.java index 76b5ecc3fc..a956612a19 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ConsumesRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ConsumesRequestCondition.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.cors.CorsUtils; @@ -70,15 +71,16 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition(parseExpressions(consumes, headers)); + Collections.sort(this.expressions); } /** - * Private constructor accepting parsed media type expressions. + * Private constructor for internal when creating matching conditions. + * Note the expressions List is neither sorted nor deep copied. */ - private ConsumesRequestCondition(Collection expressions) { - this.expressions = new ArrayList<>(expressions); - Collections.sort(this.expressions); + private ConsumesRequestCondition(List expressions) { + this.expressions = expressions; } @@ -179,9 +181,20 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition result = new LinkedHashSet<>(this.expressions); - result.removeIf(expression -> !expression.match(contentType)); - return (!result.isEmpty() ? new ConsumesRequestCondition(result) : null); + List result = getMatchingExpressions(contentType); + return !CollectionUtils.isEmpty(result) ? new ConsumesRequestCondition(result) : null; + } + + @Nullable + private List getMatchingExpressions(MediaType contentType) { + List result = null; + for (ConsumeMediaTypeExpression expression : this.expressions) { + if (expression.match(contentType)) { + result = result != null ? result : new ArrayList<>(); + result.add(expression); + } + } + return result; } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/HeadersRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/HeadersRequestCondition.java index 061305dd42..482a5ca818 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/HeadersRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/HeadersRequestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -17,7 +17,6 @@ package org.springframework.web.servlet.mvc.condition; import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import javax.servlet.http.HttpServletRequest; @@ -58,12 +57,12 @@ public final class HeadersRequestCondition extends AbstractRequestCondition conditions) { - this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions)); + private HeadersRequestCondition(Set conditions) { + this.expressions = conditions; } - private static Collection parseExpressions(String... headers) { + private static Set parseExpressions(String... headers) { Set expressions = new LinkedHashSet<>(); for (String header : headers) { HeaderExpression expr = new HeaderExpression(header); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ParamsRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ParamsRequestCondition.java index d93d6b9922..40987fa4af 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ParamsRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ParamsRequestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -18,6 +18,7 @@ package org.springframework.web.servlet.mvc.condition; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import javax.servlet.http.HttpServletRequest; @@ -142,8 +143,15 @@ public final class ParamsRequestCondition extends AbstractRequestCondition { + private final Set namesToMatch = new HashSet<>(WebUtils.SUBMIT_IMAGE_SUFFIXES.length + 1); + + ParamExpression(String expression) { super(expression); + this.namesToMatch.add(getName()); + for (String suffix : WebUtils.SUBMIT_IMAGE_SUFFIXES) { + this.namesToMatch.add(getName() + suffix); + } } @Override @@ -158,8 +166,12 @@ public final class ParamsRequestCondition extends AbstractRequestCondition fileExtensions) { this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns)); - this.pathHelper = (urlPathHelper != null ? urlPathHelper : new UrlPathHelper()); - this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher()); + this.pathHelper = urlPathHelper != null ? urlPathHelper : new UrlPathHelper(); + this.pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher(); this.useSuffixPatternMatch = useSuffixPatternMatch; this.useTrailingSlashMatch = useTrailingSlashMatch; @@ -120,6 +120,18 @@ public final class PatternsRequestCondition extends AbstractRequestCondition patterns, PatternsRequestCondition other) { + this.patterns = patterns; + this.pathHelper = other.pathHelper; + this.pathMatcher = other.pathMatcher; + this.useSuffixPatternMatch = other.useSuffixPatternMatch; + this.useTrailingSlashMatch = other.useTrailingSlashMatch; + this.fileExtensions.addAll(other.fileExtensions); + } + private static Set prependLeadingSlash(Collection patterns) { Set result = new LinkedHashSet<>(patterns.size()); @@ -175,8 +187,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition matches = getMatchingPatterns(lookupPath); - return (!matches.isEmpty() ? - new PatternsRequestCondition(matches, this.pathHelper, this.pathMatcher, - this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions) : null); + return !matches.isEmpty() ? new PatternsRequestCondition(new LinkedHashSet<>(matches), this) : null; } /** * Find the patterns matching the given lookup path. Invoking this method should - * yield results equivalent to those of calling - * {@link #getMatchingCondition(javax.servlet.http.HttpServletRequest)}. + * yield results equivalent to those of calling {@link #getMatchingCondition}. * This method is provided as an alternative to be used if no request is available * (e.g. introspection, tooling, etc). * @param lookupPath the lookup path to match to existing patterns * @return a collection of matching patterns sorted with the closest match at the top */ public List getMatchingPatterns(String lookupPath) { - List matches = new ArrayList<>(); + List matches = null; for (String pattern : this.patterns) { String match = getMatchingPattern(pattern, lookupPath); if (match != null) { + matches = matches != null ? matches : new ArrayList<>(); matches.add(match); } } + if (matches == null) { + return Collections.emptyList(); + } if (matches.size() > 1) { matches.sort(this.pathMatcher.getPatternComparator(lookupPath)); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java index 3f41cd2430..986097a0a7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java @@ -17,7 +17,6 @@ package org.springframework.web.servlet.mvc.condition; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -26,6 +25,7 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.http.MediaType; import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.HttpMediaTypeException; import org.springframework.web.HttpMediaTypeNotAcceptableException; @@ -48,6 +48,9 @@ import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.Hea */ public final class ProducesRequestCondition extends AbstractRequestCondition { + private static final ContentNegotiationManager DEFAULT_CONTENT_NEGOTIATION_MANAGER = + new ContentNegotiationManager(); + private static final ProducesRequestCondition EMPTY_CONDITION = new ProducesRequestCondition(); private static final List MEDIA_TYPE_ALL_LIST = @@ -92,18 +95,16 @@ public final class ProducesRequestCondition extends AbstractRequestCondition(parseExpressions(produces, headers)); Collections.sort(this.expressions); - this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager()); + this.contentNegotiationManager = manager != null ? manager : DEFAULT_CONTENT_NEGOTIATION_MANAGER; } /** - * Private constructor with already parsed media type expressions. + * Private constructor for internal use to create matching conditions. + * Note the expressions List is neither sorted nor deep copied. */ - private ProducesRequestCondition(Collection expressions, - @Nullable ContentNegotiationManager manager) { - - this.expressions = new ArrayList<>(expressions); - Collections.sort(this.expressions); - this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager()); + private ProducesRequestCondition(List expressions, ProducesRequestCondition other) { + this.expressions = expressions; + this.contentNegotiationManager = other.contentNegotiationManager; } @@ -198,10 +199,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition result = new LinkedHashSet<>(this.expressions); - result.removeIf(expression -> !expression.match(acceptedMediaTypes)); - if (!result.isEmpty()) { - return new ProducesRequestCondition(result, this.contentNegotiationManager); + List result = getMatchingExpressions(acceptedMediaTypes); + if (!CollectionUtils.isEmpty(result)) { + return new ProducesRequestCondition(result, this); } else if (MediaType.ALL.isPresentIn(acceptedMediaTypes)) { return EMPTY_CONDITION; @@ -211,6 +211,18 @@ public final class ProducesRequestCondition extends AbstractRequestCondition getMatchingExpressions(List acceptedMediaTypes) { + List result = null; + for (ProduceMediaTypeExpression expression : this.expressions) { + if (expression.match(acceptedMediaTypes)) { + result = result != null ? result : new ArrayList<>(); + result.add(expression); + } + } + return result; + } + /** * Compares this and another "produces" condition as follows: *
      diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java index 49956c5a2a..2ab9721e67 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -19,7 +19,9 @@ package org.springframework.web.servlet.mvc.condition; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import javax.servlet.DispatcherType; import javax.servlet.http.HttpServletRequest; @@ -40,8 +42,16 @@ import org.springframework.web.cors.CorsUtils; */ public final class RequestMethodsRequestCondition extends AbstractRequestCondition { - private static final RequestMethodsRequestCondition GET_CONDITION = - new RequestMethodsRequestCondition(RequestMethod.GET); + /** Per HTTP method cache to return ready instances from getMatchingCondition. */ + private static final Map requestMethodConditionCache; + + static { + requestMethodConditionCache = new HashMap<>(RequestMethod.values().length); + for (RequestMethod method : RequestMethod.values()) { + requestMethodConditionCache.put(method.name(), new RequestMethodsRequestCondition(method)); + } + } + private final Set methods; @@ -108,7 +118,7 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi if (RequestMethod.OPTIONS.name().equals(request.getMethod()) && !DispatcherType.ERROR.equals(request.getDispatcherType())) { - return null; // No implicit match for OPTIONS (we handle it) + return null; // We handle OPTIONS transparently, so don't match if no explicit declarations } return this; } @@ -136,11 +146,11 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi if (httpMethod != null) { for (RequestMethod method : getMethods()) { if (httpMethod.matches(method.name())) { - return new RequestMethodsRequestCondition(method); + return requestMethodConditionCache.get(method.name()); } } if (httpMethod == HttpMethod.HEAD && getMethods().contains(RequestMethod.GET)) { - return GET_CONDITION; + return requestMethodConditionCache.get(HttpMethod.GET.name()); } } return null;