提交 9c6df766 编写于 作者: J Juergen Hoeller

Related polishing

Issue. SPR-11428
上级 f9139404
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -37,9 +37,8 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
public abstract class HandlerMethodSelector {
/**
* Selects handler methods for the given handler type. Callers of this method define handler methods
* of interest through the {@link MethodFilter} parameter.
*
* Select handler methods for the given handler type.
* <p>Callers define handler methods of interest through the {@link MethodFilter} parameter.
* @param handlerType the handler type to search handler methods on
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
* @return the selected methods, or an empty set
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -43,14 +43,25 @@ import org.springframework.web.method.HandlerMethodSelector;
*/
public class ExceptionHandlerMethodResolver {
/** A filter for selecting annotated exception handling methods. */
public final static MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return (AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);
}
};
private static final Method NO_METHOD_FOUND = ClassUtils.getMethodIfAvailable(System.class, "currentTimeMillis");
private final Map<Class<? extends Throwable>, Method> mappedMethods =
new ConcurrentHashMap<Class<? extends Throwable>, Method>(16);
private final Map<Class<? extends Throwable>, Method> exceptionLookupCache =
new ConcurrentHashMap<Class<? extends Throwable>, Method>(16);
/**
* A constructor that finds {@link ExceptionHandler} methods in the given type.
* @param handlerType the type to introspect
......@@ -63,6 +74,7 @@ public class ExceptionHandlerMethodResolver {
}
}
/**
* Extract exception mappings from the {@code @ExceptionHandler} annotation
* first and as a fall-back from the method signature.
......@@ -70,9 +82,7 @@ public class ExceptionHandlerMethodResolver {
@SuppressWarnings("unchecked")
private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
detectAnnotationExceptionMappings(method, result);
if (result.isEmpty()) {
for (Class<?> paramType : method.getParameterTypes()) {
if (Throwable.class.isAssignableFrom(paramType)) {
......@@ -80,9 +90,7 @@ public class ExceptionHandlerMethodResolver {
}
}
}
Assert.notEmpty(result, "No exception types mapped to {" + method + "}");
return result;
}
......@@ -151,14 +159,4 @@ public class ExceptionHandlerMethodResolver {
}
}
/** A filter for selecting annotated exception handling methods. */
public final static MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null;
}
};
}
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -28,20 +28,13 @@ import java.util.Iterator;
*/
public abstract class AbstractRequestCondition<T extends AbstractRequestCondition<T>> implements RequestCondition<T> {
/**
* Return the discrete items a request condition is composed of.
* For example URL patterns, HTTP request methods, param expressions, etc.
* @return a collection of objects, never {@code null}
*/
protected abstract Collection<?> getContent();
@Override
public boolean equals(Object o) {
if (this == o) {
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (o != null && getClass().equals(o.getClass())) {
AbstractRequestCondition<?> other = (AbstractRequestCondition<?>) o;
if (obj != null && getClass().equals(obj.getClass())) {
AbstractRequestCondition<?> other = (AbstractRequestCondition<?>) obj;
return getContent().equals(other.getContent());
}
return false;
......@@ -66,6 +59,14 @@ public abstract class AbstractRequestCondition<T extends AbstractRequestConditio
return builder.toString();
}
/**
* Return the discrete items a request condition is composed of.
* For example URL patterns, HTTP request methods, param expressions, etc.
* @return a collection of objects, never {@code null}
*/
protected abstract Collection<?> getContent();
/**
* The notation to use when printing discrete items of content.
* For example " || " for URL patterns or " && " for param expressions.
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.Assert;
......@@ -43,6 +42,7 @@ public class CompositeRequestCondition extends AbstractRequestCondition<Composit
private final RequestConditionHolder[] requestConditions;
/**
* Create an instance with 0 or more {@code RequestCondition} types. It is
* important to create {@code CompositeRequestCondition} instances with the
......@@ -53,6 +53,11 @@ public class CompositeRequestCondition extends AbstractRequestCondition<Composit
this.requestConditions = wrap(requestConditions);
}
private CompositeRequestCondition(RequestConditionHolder[] requestConditions) {
this.requestConditions = requestConditions;
}
private RequestConditionHolder[] wrap(RequestCondition<?>... rawConditions) {
RequestConditionHolder[] wrappedConditions = new RequestConditionHolder[rawConditions.length];
for (int i = 0; i < rawConditions.length; i++) {
......@@ -61,10 +66,6 @@ public class CompositeRequestCondition extends AbstractRequestCondition<Composit
return wrappedConditions;
}
private CompositeRequestCondition(RequestConditionHolder[] requestConditions) {
this.requestConditions = requestConditions;
}
/**
* Whether this instance contains 0 conditions or not.
*/
......@@ -129,8 +130,9 @@ public class CompositeRequestCondition extends AbstractRequestCondition<Composit
private void assertNumberOfConditions(CompositeRequestCondition other) {
Assert.isTrue(getLength() == other.getLength(),
"Cannot combine CompositeRequestConditions with a different number of conditions. "
+ this.requestConditions + " and " + other.requestConditions);
"Cannot combine CompositeRequestConditions with a different number of conditions. " +
ObjectUtils.nullSafeToString(this.requestConditions) + " and " +
ObjectUtils.nullSafeToString(other.requestConditions));
}
/**
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -23,7 +23,6 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.InvalidMediaTypeException;
......@@ -49,11 +48,12 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
private final List<ConsumeMediaTypeExpression> expressions;
/**
* Creates a new instance from 0 or more "consumes" expressions.
* @param consumes expressions with the syntax described in
* {@link RequestMapping#consumes()}; if 0 expressions are provided,
* the condition will match to every request.
* the condition will match to every request
*/
public ConsumesRequestCondition(String... consumes) {
this(consumes, null);
......@@ -79,6 +79,7 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
Collections.sort(this.expressions);
}
private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, String[] headers) {
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<ConsumeMediaTypeExpression>();
if (headers != null) {
......@@ -99,6 +100,7 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
return result;
}
/**
* Return the contained MediaType expressions.
*/
......@@ -151,12 +153,10 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
* request 'Content-Type' header and returns an instance that is guaranteed
* to contain matching expressions only. The match is performed via
* {@link MediaType#includes(MediaType)}.
*
* @param request the current request
*
* @return the same instance if the condition contains no expressions;
* or a new condition with matching expressions only;
* or {@code null} if no expressions match.
* or a new condition with matching expressions only;
* or {@code null} if no expressions match.
*/
@Override
public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) {
......@@ -176,11 +176,10 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
/**
* Returns:
* <ul>
* <li>0 if the two conditions have the same number of expressions
* <li>Less than 0 if "this" has more or more specific media type expressions
* <li>Greater than 0 if "other" has more or more specific media type expressions
* <li>0 if the two conditions have the same number of expressions
* <li>Less than 0 if "this" has more or more specific media type expressions
* <li>Greater than 0 if "other" has more or more specific media type expressions
* </ul>
*
* <p>It is assumed that both instances have been obtained via
* {@link #getMatchingCondition(HttpServletRequest)} and each instance contains
* the matching consumable media type expression only or is otherwise empty.
......@@ -201,6 +200,7 @@ public final class ConsumesRequestCondition extends AbstractRequestCondition<Con
}
}
/**
* Parses and matches a single media type expression to a request's 'Content-Type' header.
*/
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -20,7 +20,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -41,12 +40,13 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
private final Set<HeaderExpression> expressions;
/**
* Create a new instance from the given header expressions. Expressions with
* header names 'Accept' or 'Content-Type' are ignored. See {@link ConsumesRequestCondition}
* and {@link ProducesRequestCondition} for those.
* @param headers media type expressions with syntax defined in {@link RequestMapping#headers()};
* if 0, the condition will match to every request.
* if 0, the condition will match to every request
*/
public HeadersRequestCondition(String... headers) {
this(parseExpressions(headers));
......@@ -56,6 +56,7 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
this.expressions = Collections.unmodifiableSet(new LinkedHashSet<HeaderExpression>(conditions));
}
private static Collection<HeaderExpression> parseExpressions(String... headers) {
Set<HeaderExpression> expressions = new LinkedHashSet<HeaderExpression>();
if (headers != null) {
......@@ -115,11 +116,10 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
/**
* Returns:
* <ul>
* <li>0 if the two conditions have the same number of header expressions
* <li>Less than 0 if "this" instance has more header expressions
* <li>Greater than 0 if the "other" instance has more header expressions
* <li>0 if the two conditions have the same number of header expressions
* <li>Less than 0 if "this" instance has more header expressions
* <li>Greater than 0 if the "other" instance has more header expressions
* </ul>
*
* <p>It is assumed that both instances have been obtained via
* {@link #getMatchingCondition(HttpServletRequest)} and each instance
* contains the matching header expression only or is otherwise empty.
......@@ -129,6 +129,7 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
return other.expressions.size() - this.expressions.size();
}
/**
* Parses and matches a single header expression to a request.
*/
......@@ -161,4 +162,5 @@ public final class HeadersRequestCondition extends AbstractRequestCondition<Head
return result;
}
}
}
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -20,7 +20,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
......@@ -38,6 +37,7 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
private final Set<ParamExpression> expressions;
/**
* Create a new instance from the given param expressions.
* @param params expressions with syntax defined in {@link RequestMapping#params()};
......@@ -51,6 +51,7 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
this.expressions = Collections.unmodifiableSet(new LinkedHashSet<ParamExpression>(conditions));
}
private static Collection<ParamExpression> parseExpressions(String... params) {
Set<ParamExpression> expressions = new LinkedHashSet<ParamExpression>();
if (params != null) {
......@@ -61,6 +62,7 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
return expressions;
}
/**
* Return the contained request parameter expressions.
*/
......@@ -70,7 +72,7 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
@Override
protected Collection<ParamExpression> getContent() {
return expressions;
return this.expressions;
}
@Override
......@@ -106,20 +108,20 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
/**
* Returns:
* <ul>
* <li>0 if the two conditions have the same number of parameter expressions
* <li>Less than 0 if "this" instance has more parameter expressions
* <li>Greater than 0 if the "other" instance has more parameter expressions
* <li>0 if the two conditions have the same number of parameter expressions
* <li>Less than 0 if "this" instance has more parameter expressions
* <li>Greater than 0 if the "other" instance has more parameter expressions
* </ul>
*
* <p>It is assumed that both instances have been obtained via
* {@link #getMatchingCondition(HttpServletRequest)} and each instance
* contains the matching parameter expressions only or is otherwise empty.
*/
@Override
public int compareTo(ParamsRequestCondition other, HttpServletRequest request) {
return other.expressions.size() - this.expressions.size();
return (other.expressions.size() - this.expressions.size());
}
/**
* Parses and matches a single param expression to a request.
*/
......
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
......@@ -154,10 +154,10 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* Returns a new instance with URL patterns from the current instance ("this") and
* the "other" instance as follows:
* <ul>
* <li>If there are patterns in both instances, combine the patterns in "this" with
* the patterns in "other" using {@link PathMatcher#combine(String, String)}.
* <li>If only one instance has patterns, use them.
* <li>If neither instance has patterns, use an empty String (i.e. "").
* <li>If there are patterns in both instances, combine the patterns in "this" with
* the patterns in "other" using {@link PathMatcher#combine(String, String)}.
* <li>If only one instance has patterns, use them.
* <li>If neither instance has patterns, use an empty String (i.e. "").
* </ul>
*/
@Override
......@@ -189,10 +189,10 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
* {@link PathMatcher#getPatternComparator(String)}.
* <p>A matching pattern is obtained by making checks in the following order:
* <ul>
* <li>Direct match
* <li>Pattern match with ".*" appended if the pattern doesn't already contain a "."
* <li>Pattern match
* <li>Pattern match with "/" appended if the pattern doesn't already end in "/"
* <li>Direct match
* <li>Pattern match with ".*" appended if the pattern doesn't already contain a "."
* <li>Pattern match
* <li>Pattern match with "/" appended if the pattern doesn't already end in "/"
* </ul>
* @param request the current request
* @return the same instance if the condition contains no patterns;
......@@ -264,7 +264,6 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath);
Iterator<String> iterator = this.patterns.iterator();
Iterator<String> iteratorOther = other.patterns.iterator();
while (iterator.hasNext() && iteratorOther.hasNext()) {
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -23,7 +23,6 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
......@@ -46,10 +45,14 @@ import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.Hea
*/
public final class ProducesRequestCondition extends AbstractRequestCondition<ProducesRequestCondition> {
private final List<ProduceMediaTypeExpression> MEDIA_TYPE_ALL_LIST =
Collections.singletonList(new ProduceMediaTypeExpression("*/*"));
private final List<ProduceMediaTypeExpression> expressions;
private final ContentNegotiationManager contentNegotiationManager;
/**
* Creates a new instance from "produces" expressions. If 0 expressions
* are provided in total, this condition will match to any request.
......@@ -78,14 +81,22 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
* @param headers expressions with syntax defined by {@link RequestMapping#headers()}
* @param manager used to determine requested media types
*/
public ProducesRequestCondition(String[] produces, String[] headers,
ContentNegotiationManager manager) {
public ProducesRequestCondition(String[] produces, String[] headers, ContentNegotiationManager manager) {
this.expressions = new ArrayList<ProduceMediaTypeExpression>(parseExpressions(produces, headers));
Collections.sort(this.expressions);
this.contentNegotiationManager = (manager != null) ? manager : new ContentNegotiationManager();
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
}
/**
* Private constructor with already parsed media type expressions.
*/
private ProducesRequestCondition(Collection<ProduceMediaTypeExpression> expressions, ContentNegotiationManager manager) {
this.expressions = new ArrayList<ProduceMediaTypeExpression>(expressions);
Collections.sort(this.expressions);
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
}
private Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, String[] headers) {
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<ProduceMediaTypeExpression>();
if (headers != null) {
......@@ -106,17 +117,6 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
return result;
}
/**
* Private constructor with already parsed media type expressions.
*/
private ProducesRequestCondition(Collection<ProduceMediaTypeExpression> expressions,
ContentNegotiationManager manager) {
this.expressions = new ArrayList<ProduceMediaTypeExpression>(expressions);
Collections.sort(this.expressions);
this.contentNegotiationManager = (manager != null) ? manager : new ContentNegotiationManager();
}
/**
* Return the contained "produces" expressions.
*/
......@@ -169,12 +169,10 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
* request 'Content-Type' header and returns an instance that is guaranteed
* to contain matching expressions only. The match is performed via
* {@link MediaType#isCompatibleWith(MediaType)}.
*
* @param request the current request
*
* @return the same instance if there are no expressions;
* or a new condition with matching expressions;
* or {@code null} if no expressions match.
* or a new condition with matching expressions;
* or {@code null} if no expressions match.
*/
@Override
public ProducesRequestCondition getMatchingCondition(HttpServletRequest request) {
......@@ -193,18 +191,16 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
/**
* Compares this and another "produces" condition as follows:
*
* <ol>
* <li>Sort 'Accept' header media types by quality value via
* {@link MediaType#sortByQualityValue(List)} and iterate the list.
* <li>Get the first index of matching media types in each "produces"
* condition first matching with {@link MediaType#equals(Object)} and
* then with {@link MediaType#includes(MediaType)}.
* <li>If a lower index is found, the condition at that index wins.
* <li>If both indexes are equal, the media types at the index are
* compared further with {@link MediaType#SPECIFICITY_COMPARATOR}.
* <li>Sort 'Accept' header media types by quality value via
* {@link MediaType#sortByQualityValue(List)} and iterate the list.
* <li>Get the first index of matching media types in each "produces"
* condition first matching with {@link MediaType#equals(Object)} and
* then with {@link MediaType#includes(MediaType)}.
* <li>If a lower index is found, the condition at that index wins.
* <li>If both indexes are equal, the media types at the index are
* compared further with {@link MediaType#SPECIFICITY_COMPARATOR}.
* </ol>
*
* <p>It is assumed that both instances have been obtained via
* {@link #getMatchingCondition(HttpServletRequest)} and each instance
* contains the matching producible media type expression only or
......@@ -214,7 +210,6 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
public int compareTo(ProducesRequestCondition other, HttpServletRequest request) {
try {
List<MediaType> acceptedMediaTypes = getAcceptedMediaTypes(request);
for (MediaType acceptedMediaType : acceptedMediaTypes) {
int thisIndex = this.indexOfEqualMediaType(acceptedMediaType);
int otherIndex = other.indexOfEqualMediaType(acceptedMediaType);
......@@ -262,14 +257,14 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
return -1;
}
private int compareMatchingMediaTypes(ProducesRequestCondition condition1,
int index1, ProducesRequestCondition condition2, int index2) {
private int compareMatchingMediaTypes(ProducesRequestCondition condition1, int index1,
ProducesRequestCondition condition2, int index2) {
int result = 0;
if (index1 != index2) {
result = index2 - index1;
}
else if (index1 != -1 && index2 != -1) {
else if (index1 != -1) {
ProduceMediaTypeExpression expr1 = condition1.getExpressionsToCompare().get(index1);
ProduceMediaTypeExpression expr2 = condition2.getExpressionsToCompare().get(index2);
result = expr1.compareTo(expr2);
......@@ -283,12 +278,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
* with a {@code MediaType_ALL} expression.
*/
private List<ProduceMediaTypeExpression> getExpressionsToCompare() {
return this.expressions.isEmpty() ? MEDIA_TYPE_ALL_LIST : this.expressions;
return (this.expressions.isEmpty() ? MEDIA_TYPE_ALL_LIST : this.expressions);
}
private final List<ProduceMediaTypeExpression> MEDIA_TYPE_ALL_LIST =
Collections.singletonList(new ProduceMediaTypeExpression("*/*"));
/**
* Parses and matches a single media type expression to a request's 'Accept' header.
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -21,15 +21,14 @@ import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* The contract for request conditions.
* The contract for request conditions in Spring MVC's mapping infrastructure.
*
* <p>Request conditions can be combined via {@link #combine(Object)}, matched to
* a request via {@link #getMatchingCondition(HttpServletRequest)}, and compared
* to each other via {@link #compareTo(Object, HttpServletRequest)} to determine
* which matches a request more closely.
*
* @param <T> The type of objects that this RequestCondition can be combined
* with compared to.
* @param <T> the type of objects that this RequestCondition can be combined with and compared to
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
......@@ -41,10 +40,9 @@ public interface RequestCondition<T> {
* Defines the rules for combining this condition (i.e. the current instance)
* with another condition. For example combining type- and method-level
* {@link RequestMapping} conditions.
*
* @param other the condition to combine with.
* @return a request condition instance that is the result of combining
* the two condition instances.
* the two condition instances.
*/
T combine(T other);
......@@ -54,9 +52,8 @@ public interface RequestCondition<T> {
* current request. For example a condition with URL patterns might
* return a new condition that contains matching patterns sorted
* with best matching patterns on top.
*
* @return a condition instance in case of a match;
* or {@code null} if there is no match.
* or {@code null} if there is no match
*/
T getMatchingCondition(HttpServletRequest request);
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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,7 +18,6 @@ package org.springframework.web.servlet.mvc.condition;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
/**
......@@ -39,6 +38,7 @@ public final class RequestConditionHolder extends AbstractRequestCondition<Reque
private final RequestCondition<Object> condition;
/**
* Create a new holder to wrap the given request condition.
* @param requestCondition the condition to hold, may be {@code null}
......@@ -48,6 +48,7 @@ public final class RequestConditionHolder extends AbstractRequestCondition<Reque
this.condition = (RequestCondition<Object>) requestCondition;
}
/**
* Return the held request condition, or {@code null} if not holding one.
*/
......@@ -57,7 +58,7 @@ public final class RequestConditionHolder extends AbstractRequestCondition<Reque
@Override
protected Collection<?> getContent() {
return this.condition != null ? Collections.singleton(this.condition) : Collections.emptyList();
return (this.condition != null ? Collections.singleton(this.condition) : Collections.emptyList());
}
@Override
......@@ -110,7 +111,7 @@ public final class RequestConditionHolder extends AbstractRequestCondition<Reque
return this;
}
RequestCondition<?> match = (RequestCondition<?>) this.condition.getMatchingCondition(request);
return (match != null) ? new RequestConditionHolder(match) : null;
return (match != null ? new RequestConditionHolder(match) : null);
}
/**
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2014 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.
......@@ -22,7 +22,6 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMethod;
......@@ -39,36 +38,36 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
private final Set<RequestMethod> methods;
/**
* Create a new instance with the given request methods.
* @param requestMethods 0 or more HTTP request methods;
* if, 0 the condition will match to every request.
* if, 0 the condition will match to every request
*/
public RequestMethodsRequestCondition(RequestMethod... requestMethods) {
this(asList(requestMethods));
}
private static List<RequestMethod> asList(RequestMethod... requestMethods) {
return requestMethods != null ? Arrays.asList(requestMethods) : Collections.<RequestMethod>emptyList();
}
/**
* Private constructor.
*/
private RequestMethodsRequestCondition(Collection<RequestMethod> requestMethods) {
this.methods = Collections.unmodifiableSet(new LinkedHashSet<RequestMethod>(requestMethods));
}
private static List<RequestMethod> asList(RequestMethod... requestMethods) {
return (requestMethods != null ? Arrays.asList(requestMethods) : Collections.<RequestMethod>emptyList());
}
/**
* Returns all {@link RequestMethod}s contained in this condition.
*/
public Set<RequestMethod> getMethods() {
return methods;
return this.methods;
}
@Override
protected Collection<RequestMethod> getContent() {
return methods;
return this.methods;
}
@Override
......@@ -123,18 +122,17 @@ public final class RequestMethodsRequestCondition extends AbstractRequestConditi
/**
* Returns:
* <ul>
* <li>0 if the two conditions contain the same number of HTTP request methods
* <li>Less than 0 if "this" instance has an HTTP request method but "other" doesn't
* <li>Greater than 0 "other" has an HTTP request method but "this" doesn't
* <li>0 if the two conditions contain the same number of HTTP request methods
* <li>Less than 0 if "this" instance has an HTTP request method but "other" doesn't
* <li>Greater than 0 "other" has an HTTP request method but "this" doesn't
* </ul>
*
* <p>It is assumed that both instances have been obtained via
* {@link #getMatchingCondition(HttpServletRequest)} and therefore each instance
* contains the matching HTTP request method only or is otherwise empty.
*/
@Override
public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) {
return other.methods.size() - this.methods.size();
return (other.methods.size() - this.methods.size());
}
}
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
......@@ -16,9 +16,15 @@
package org.springframework.web.servlet.mvc.method;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
......@@ -45,7 +51,7 @@ import org.springframework.web.util.WebUtils;
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1.0
* @since 3.1
*/
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
......
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
......@@ -78,7 +78,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
/**
* Whether to use suffix pattern match for registered file extensions only
* when matching patterns to requests.
*
* <p>If enabled, a controller method mapped to "/users" also matches to
* "/users.json" assuming ".json" is a file extension registered with the
* provided {@link #setContentNegotiationManager(ContentNegotiationManager)
......@@ -87,14 +86,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* can lead to ambiguous interpretation of path variable content, (e.g. given
* "/users/{user}" and incoming URLs such as "/users/john.j.joe" and
* "/users/john.j.joe.json").
*
* <p>If enabled, this flag also enables
* {@link #setUseSuffixPatternMatch(boolean) useSuffixPatternMatch}. The
* default value is {@code false}.
*/
public void setUseRegisteredSuffixPatternMatch(boolean useRegisteredSuffixPatternMatch) {
this.useRegisteredSuffixPatternMatch = useRegisteredSuffixPatternMatch;
this.useSuffixPatternMatch = useRegisteredSuffixPatternMatch ? true : this.useSuffixPatternMatch;
this.useSuffixPatternMatch = (useRegisteredSuffixPatternMatch || this.useSuffixPatternMatch);
}
/**
......@@ -106,20 +104,30 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
this.useTrailingSlashMatch = useTrailingSlashMatch;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
/**
* Set the {@link ContentNegotiationManager} to use to determine requested media types.
* If not set, the default constructor is used.
*/
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
Assert.notNull(contentNegotiationManager);
Assert.notNull(contentNegotiationManager, "ContentNegotiationManager must not be null");
this.contentNegotiationManager = contentNegotiationManager;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
@Override
public void afterPropertiesSet() {
if (this.useRegisteredSuffixPatternMatch) {
this.fileExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
}
super.afterPropertiesSet();
}
/**
* Whether to use suffix pattern matching.
*/
......@@ -131,11 +139,11 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* Whether to use registered suffixes for pattern matching.
*/
public boolean useRegisteredSuffixPatternMatch() {
return useRegisteredSuffixPatternMatch;
return this.useRegisteredSuffixPatternMatch;
}
/**
* Whether to match to URLs irrespective of the presence of a trailing slash.
* Whether to match to URLs irrespective of the presence of a trailing slash.
*/
public boolean useTrailingSlashMatch() {
return this.useTrailingSlashMatch;
......@@ -155,13 +163,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
return this.fileExtensions;
}
@Override
public void afterPropertiesSet() {
if (this.useRegisteredSuffixPatternMatch) {
this.fileExtensions.addAll(contentNegotiationManager.getAllFileExtensions());
}
super.afterPropertiesSet();
}
/**
* {@inheritDoc}
......@@ -176,10 +177,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
/**
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
*
* @return the created RequestMappingInfo, or {@code null} if the method
* does not have a {@code @RequestMapping} annotation.
*
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
......@@ -204,11 +203,9 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* The custom {@link RequestCondition} can be of any type so long as the
* same condition type is returned from all calls to this method in order
* to ensure custom request conditions can be combined and compared.
*
* <p>Consider extending {@link AbstractRequestCondition} for custom
* condition types and using {@link CompositeRequestCondition} to provide
* multiple custom conditions.
*
* @param handlerType the handler type for which to create the condition
* @return the condition, or {@code null}
*/
......@@ -221,11 +218,9 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
* The custom {@link RequestCondition} can be of any type so long as the
* same condition type is returned from all calls to this method in order
* to ensure custom request conditions can be combined and compared.
*
* <p>Consider extending {@link AbstractRequestCondition} for custom
* condition types and using {@link CompositeRequestCondition} to provide
* multiple custom conditions.
*
* @param method the handler method for which to create the condition
* @return the condition, or {@code null}
*/
......@@ -236,9 +231,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
/**
* Created a RequestMappingInfo from a RequestMapping annotation.
*/
protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation,
RequestCondition<?> customCondition) {
protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
return new RequestMappingInfo(
new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
......@@ -247,7 +240,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
new ParamsRequestCondition(annotation.params()),
new HeadersRequestCondition(annotation.headers()),
new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()),
new ProducesRequestCondition(annotation.produces(), annotation.headers(), this.contentNegotiationManager),
customCondition);
}
......@@ -261,7 +254,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
}
else {
String[] resolvedPatterns = new String[patterns.length];
for (int i=0; i < patterns.length; i++) {
for (int i = 0; i < patterns.length; i++) {
resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
}
return resolvedPatterns;
......
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
......@@ -13,12 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
package org.springframework.web.servlet.mvc.method.annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
......@@ -29,6 +25,7 @@ import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringValueResolver;
......@@ -38,6 +35,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import static org.junit.Assert.*;
/**
* Tests for {@link RequestMappingHandlerMapping}.
*
......@@ -47,14 +46,16 @@ public class RequestMappingHandlerMappingTests {
private RequestMappingHandlerMapping handlerMapping;
@Before
public void setup() {
this.handlerMapping = new RequestMappingHandlerMapping();
this.handlerMapping.setApplicationContext(new StaticWebApplicationContext());
}
@Test
public void useRegsiteredSuffixPatternMatch() {
public void useRegisteredSuffixPatternMatch() {
assertTrue(this.handlerMapping.useSuffixPatternMatch());
assertFalse(this.handlerMapping.useRegisteredSuffixPatternMatch());
......@@ -72,8 +73,7 @@ public class RequestMappingHandlerMappingTests {
}
@Test
public void useRegsiteredSuffixPatternMatchInitialization() {
public void useRegisteredSuffixPatternMatchInitialization() {
Map<String, MediaType> fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON);
PathExtensionContentNegotiationStrategy strategy = new PathExtensionContentNegotiationStrategy(fileExtensions);
ContentNegotiationManager manager = new ContentNegotiationManager(strategy);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册