Browse Source

RequestCondition implementations minor refactoring

Reduce object creation by pre-computing instances that can be re-used,
and eliminating collection copying and sorting where not needed.

See gh-22644
pull/22737/head
Rossen Stoyanchev 6 years ago
parent
commit
bb9fcad58a
  1. 30
      spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ConsumesRequestCondition.java
  2. 9
      spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/HeadersRequestCondition.java
  3. 2
      spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java
  4. 39
      spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ProducesRequestCondition.java
  5. 28
      spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java
  6. 2
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/AbstractNameValueExpression.java
  7. 29
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ConsumesRequestCondition.java
  8. 9
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/HeadersRequestCondition.java
  9. 18
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ParamsRequestCondition.java
  10. 32
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java
  11. 38
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java
  12. 22
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java

30
spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ConsumesRequestCondition.java

@ -25,6 +25,8 @@ import java.util.Set; @@ -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<Con @@ -68,15 +70,16 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
* @param headers as described in {@link RequestMapping#headers()}
*/
public ConsumesRequestCondition(String[] consumes, String[] headers) {
this(parseExpressions(consumes, headers));
this.expressions = new ArrayList<>(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<ConsumeMediaTypeExpression> expressions) {
this.expressions = new ArrayList<>(expressions);
Collections.sort(this.expressions);
private ConsumesRequestCondition(List<ConsumeMediaTypeExpression> expressions) {
this.expressions = expressions;
}
@ -166,9 +169,20 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con @@ -166,9 +169,20 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
if (isEmpty()) {
return this;
}
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>(this.expressions);
result.removeIf(expression -> !expression.match(exchange));
return (!result.isEmpty() ? new ConsumesRequestCondition(result) : null);
List<ConsumeMediaTypeExpression> result = getMatchingExpressions(exchange);
return !CollectionUtils.isEmpty(result) ? new ConsumesRequestCondition(result) : null;
}
@Nullable
private List<ConsumeMediaTypeExpression> getMatchingExpressions(ServerWebExchange exchange) {
List<ConsumeMediaTypeExpression> result = null;
for (ConsumeMediaTypeExpression expression : this.expressions) {
if (expression.match(exchange)) {
result = result != null ? result : new ArrayList<>();
result.add(expression);
}
}
return result;
}
/**

9
spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/HeadersRequestCondition.java

@ -1,5 +1,5 @@ @@ -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 @@ @@ -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<Head @@ -56,12 +55,12 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
this(parseExpressions(headers));
}
private HeadersRequestCondition(Collection<HeaderExpression> conditions) {
this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions));
private HeadersRequestCondition(Set<HeaderExpression> conditions) {
this.expressions = conditions;
}
private static Collection<HeaderExpression> parseExpressions(String... headers) {
private static Set<HeaderExpression> parseExpressions(String... headers) {
Set<HeaderExpression> expressions = new LinkedHashSet<>();
if (headers != null) {
for (String header : headers) {

2
spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java

@ -1,5 +1,5 @@ @@ -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.

39
spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/ProducesRequestCondition.java

@ -17,7 +17,6 @@ @@ -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; @@ -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; @@ -48,6 +47,9 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException;
*/
public final class ProducesRequestCondition extends AbstractRequestCondition<ProducesRequestCondition> {
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<Pro @@ -90,18 +92,16 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
public ProducesRequestCondition(String[] produces, String[] headers, RequestedContentTypeResolver resolver) {
this.expressions = new ArrayList<>(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<ProduceMediaTypeExpression> expressions,
RequestedContentTypeResolver resolver) {
this.expressions = new ArrayList<>(expressions);
Collections.sort(this.expressions);
this.contentTypeResolver = (resolver != null ? resolver : new RequestedContentTypeResolverBuilder().build());
private ProducesRequestCondition(List<ProduceMediaTypeExpression> expressions, ProducesRequestCondition other) {
this.expressions = expressions;
this.contentTypeResolver = other.contentTypeResolver;
}
@ -189,10 +189,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro @@ -189,10 +189,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
if (isEmpty() || CorsUtils.isPreFlightRequest(exchange.getRequest())) {
return EMPTY_CONDITION;
}
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>(this.expressions);
result.removeIf(expression -> !expression.match(exchange));
if (!result.isEmpty()) {
return new ProducesRequestCondition(result, this.contentTypeResolver);
List<ProduceMediaTypeExpression> result = getMatchingExpressions(exchange);
if (!CollectionUtils.isEmpty(result)) {
return new ProducesRequestCondition(result, this);
}
else {
try {
@ -207,6 +206,18 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro @@ -207,6 +206,18 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
return null;
}
@Nullable
private List<ProduceMediaTypeExpression> getMatchingExpressions(ServerWebExchange exchange) {
List<ProduceMediaTypeExpression> 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:
* <ol>

28
spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/RequestMethodsRequestCondition.java

@ -19,8 +19,10 @@ package org.springframework.web.reactive.result.condition; @@ -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; @@ -39,8 +41,15 @@ import org.springframework.web.server.ServerWebExchange;
*/
public final class RequestMethodsRequestCondition extends AbstractRequestCondition<RequestMethodsRequestCondition> {
private static final RequestMethodsRequestCondition GET_CONDITION =
new RequestMethodsRequestCondition(RequestMethod.GET);
/** Per HTTP method cache to return ready instances from getMatchingCondition. */
private static final Map<String, RequestMethodsRequestCondition> requestMethodConditionCache;
static {
requestMethodConditionCache = new HashMap<>(RequestMethod.values().length);
for (RequestMethod method : RequestMethod.values()) {
requestMethodConditionCache.put(method.name(), new RequestMethodsRequestCondition(method));
}
}
private final Set<RequestMethod> methods;
@ -110,11 +119,11 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi @@ -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 @@ -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;

2
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/AbstractNameValueExpression.java

@ -80,7 +80,7 @@ abstract class AbstractNameValueExpression<T> implements NameValueExpression<T> @@ -80,7 +80,7 @@ abstract class AbstractNameValueExpression<T> implements NameValueExpression<T>
else {
isMatch = matchName(request);
}
return (this.isNegated ? !isMatch : isMatch);
return this.isNegated != isMatch;
}

29
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ConsumesRequestCondition.java

@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletRequest; @@ -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<Con @@ -70,15 +71,16 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
* @param headers as described in {@link RequestMapping#headers()}
*/
public ConsumesRequestCondition(String[] consumes, @Nullable String[] headers) {
this(parseExpressions(consumes, headers));
this.expressions = new ArrayList<>(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<ConsumeMediaTypeExpression> expressions) {
this.expressions = new ArrayList<>(expressions);
Collections.sort(this.expressions);
private ConsumesRequestCondition(List<ConsumeMediaTypeExpression> expressions) {
this.expressions = expressions;
}
@ -179,9 +181,20 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con @@ -179,9 +181,20 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
return null;
}
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>(this.expressions);
result.removeIf(expression -> !expression.match(contentType));
return (!result.isEmpty() ? new ConsumesRequestCondition(result) : null);
List<ConsumeMediaTypeExpression> result = getMatchingExpressions(contentType);
return !CollectionUtils.isEmpty(result) ? new ConsumesRequestCondition(result) : null;
}
@Nullable
private List<ConsumeMediaTypeExpression> getMatchingExpressions(MediaType contentType) {
List<ConsumeMediaTypeExpression> result = null;
for (ConsumeMediaTypeExpression expression : this.expressions) {
if (expression.match(contentType)) {
result = result != null ? result : new ArrayList<>();
result.add(expression);
}
}
return result;
}
/**

9
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/HeadersRequestCondition.java

@ -1,5 +1,5 @@ @@ -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 @@ @@ -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<Head @@ -58,12 +57,12 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
this(parseExpressions(headers));
}
private HeadersRequestCondition(Collection<HeaderExpression> conditions) {
this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions));
private HeadersRequestCondition(Set<HeaderExpression> conditions) {
this.expressions = conditions;
}
private static Collection<HeaderExpression> parseExpressions(String... headers) {
private static Set<HeaderExpression> parseExpressions(String... headers) {
Set<HeaderExpression> expressions = new LinkedHashSet<>();
for (String header : headers) {
HeaderExpression expr = new HeaderExpression(header);

18
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ParamsRequestCondition.java

@ -1,5 +1,5 @@ @@ -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; @@ -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<Param @@ -142,8 +143,15 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
*/
static class ParamExpression extends AbstractNameValueExpression<String> {
private final Set<String> 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<Param @@ -158,8 +166,12 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
@Override
protected boolean matchName(HttpServletRequest request) {
return (WebUtils.hasSubmitParameter(request, this.name) ||
request.getParameterMap().containsKey(this.name));
for (String current : this.namesToMatch) {
if (request.getParameterMap().get(current) != null) {
return true;
}
}
return request.getParameterMap().containsKey(this.name);
}
@Override

32
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java

@ -105,8 +105,8 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat @@ -105,8 +105,8 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
boolean useTrailingSlashMatch, @Nullable List<String> 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<Pat @@ -120,6 +120,18 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
}
}
/**
* Private constructor for use when combining and matching.
*/
private PatternsRequestCondition(Set<String> 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<String> prependLeadingSlash(Collection<String> patterns) {
Set<String> result = new LinkedHashSet<>(patterns.size());
@ -175,8 +187,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat @@ -175,8 +187,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
else {
result.add("");
}
return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher,
this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions);
return new PatternsRequestCondition(result, this);
}
/**
@ -203,28 +214,29 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat @@ -203,28 +214,29 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
}
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
List<String> 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<String> getMatchingPatterns(String lookupPath) {
List<String> matches = new ArrayList<>();
List<String> 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));
}

38
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/ProducesRequestCondition.java

@ -17,7 +17,6 @@ @@ -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; @@ -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 @@ -48,6 +48,9 @@ import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.Hea
*/
public final class ProducesRequestCondition extends AbstractRequestCondition<ProducesRequestCondition> {
private static final ContentNegotiationManager DEFAULT_CONTENT_NEGOTIATION_MANAGER =
new ContentNegotiationManager();
private static final ProducesRequestCondition EMPTY_CONDITION = new ProducesRequestCondition();
private static final List<ProduceMediaTypeExpression> MEDIA_TYPE_ALL_LIST =
@ -92,18 +95,16 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro @@ -92,18 +95,16 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
this.expressions = new ArrayList<>(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<ProduceMediaTypeExpression> expressions,
@Nullable ContentNegotiationManager manager) {
this.expressions = new ArrayList<>(expressions);
Collections.sort(this.expressions);
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
private ProducesRequestCondition(List<ProduceMediaTypeExpression> expressions, ProducesRequestCondition other) {
this.expressions = expressions;
this.contentNegotiationManager = other.contentNegotiationManager;
}
@ -198,10 +199,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro @@ -198,10 +199,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
return null;
}
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>(this.expressions);
result.removeIf(expression -> !expression.match(acceptedMediaTypes));
if (!result.isEmpty()) {
return new ProducesRequestCondition(result, this.contentNegotiationManager);
List<ProduceMediaTypeExpression> 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<Pro @@ -211,6 +211,18 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
}
}
@Nullable
private List<ProduceMediaTypeExpression> getMatchingExpressions(List<MediaType> acceptedMediaTypes) {
List<ProduceMediaTypeExpression> 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:
* <ol>

22
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/RequestMethodsRequestCondition.java

@ -1,5 +1,5 @@ @@ -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; @@ -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; @@ -40,8 +42,16 @@ import org.springframework.web.cors.CorsUtils;
*/
public final class RequestMethodsRequestCondition extends AbstractRequestCondition<RequestMethodsRequestCondition> {
private static final RequestMethodsRequestCondition GET_CONDITION =
new RequestMethodsRequestCondition(RequestMethod.GET);
/** Per HTTP method cache to return ready instances from getMatchingCondition. */
private static final Map<String, RequestMethodsRequestCondition> requestMethodConditionCache;
static {
requestMethodConditionCache = new HashMap<>(RequestMethod.values().length);
for (RequestMethod method : RequestMethod.values()) {
requestMethodConditionCache.put(method.name(), new RequestMethodsRequestCondition(method));
}
}
private final Set<RequestMethod> methods;
@ -108,7 +118,7 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi @@ -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 @@ -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;

Loading…
Cancel
Save