SWF-8214 javadoc updates

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4218 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Rossen Stoyanchev 2011-04-12 13:23:14 +00:00
parent ab4a3568b1
commit 19fdaaa74a
3 changed files with 119 additions and 46 deletions

View File

@ -36,7 +36,21 @@ import org.springframework.web.method.HandlerMethodSelector;
/** /**
* Abstract base class for {@link org.springframework.web.servlet.HandlerMapping HandlerMapping} implementations that * Abstract base class for {@link org.springframework.web.servlet.HandlerMapping HandlerMapping} implementations that
* support {@link HandlerMethod}s. * support mapping requests to {@link HandlerMethod}s rather than to handlers.
*
* <p>Each {@link HandlerMethod} is registered with a unique key. Subclasses define the key type and how to create it
* for a given handler method. Keys represent conditions for matching a handler method to a request.
*
* <p>Subclasses must also define how to create a key for an incoming request. The resulting key is used to perform
* a {@link HandlerMethod} lookup possibly resulting in a direct match. However, when a map lookup is insufficient,
* the keys of all handler methods are iterated and subclasses are allowed to make an exhaustive check of key
* conditions against the request.
*
* <p>Since there can be more than one matching key for a request, subclasses must define a comparator for sorting
* the keys of matching handler methods in order to find the most specific match.
*
* @param <T> A unique key for the registration of mapped {@link HandlerMethod}s representing the conditions to
* match a handler method to a request.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -80,6 +94,9 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
*/ */
protected abstract boolean isHandler(String beanName); protected abstract boolean isHandler(String beanName);
/**
* Detect and register handler methods for the specified handler.
*/
private void detectHandlerMethods(final String handlerName) { private void detectHandlerMethods(final String handlerName) {
Class<?> handlerType = getApplicationContext().getType(handlerName); Class<?> handlerType = getApplicationContext().getType(handlerName);

View File

@ -33,10 +33,18 @@ import org.springframework.web.servlet.mvc.method.condition.RequestConditionFact
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
/** /**
* TODO * Contains a set of conditions to match to a given request such as URL patterns, HTTP methods, request
* parameters and headers.
*
* <p>A {@link RequestKey} can be combined with another {@link RequestKey} resulting in a new {@link RequestKey}
* with conditions from both (see {@link #combine(RequestKey, PathMatcher)}).
*
* <p>A {@link RequestKey} can be matched to a request resulting in a new {@link RequestKey} with the subset of
* conditions relevant to the request (see {@link #getMatchingKey(HttpServletRequest, PathMatcher, UrlPathHelper)}).
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1
*/ */
public final class RequestKey { public final class RequestKey {
@ -52,14 +60,19 @@ public final class RequestKey {
private int hash; private int hash;
/**
* Creates a new {@code RequestKey} instance with the given URL patterns and HTTP methods.
*
* <p>Package protected for testing purposes.
*/
RequestKey(Collection<String> patterns, Collection<RequestMethod> methods) { RequestKey(Collection<String> patterns, Collection<RequestMethod> methods) {
this(patterns, methods, null, null, null); this(patterns, methods, null, null, null);
} }
/** /**
* Creates a new {@code RequestKey} instance with the given parameters. * Creates a new {@code RequestKey} instance with a full set of conditions.
* *
* <p/>Package protected for testing purposes. * <p>Package protected for testing purposes.
*/ */
RequestKey(Collection<String> patterns, RequestKey(Collection<String> patterns,
Collection<RequestMethod> methods, Collection<RequestMethod> methods,
@ -152,12 +165,22 @@ public final class RequestKey {
} }
/** /**
* Combines this {@code RequestKey} with another. The typical use case for this is combining type * Combines this {@code RequestKey} with another as follows:
* and method-level {@link RequestMapping @RequestMapping} annotations. * <ul>
* * <li>URL patterns:
* @param methodKey the method-level RequestKey * <ul>
* <li>If both keys have path patterns combine them according to the rules of the given {@link PathMatcher}.
* <li>If either key contains path patterns but not both use only what is available.
* <li>If neither key contains path patterns use "/".
* </ul>
* <li>HTTP methods are combined as union of all HTTP methods listed in both keys.
* <li>Request parameter are combined into a logical AND.
* <li>Request header are combined into a logical AND.
* <li>Consumes .. TODO
* </ul>
* @param methodKey the key to combine with
* @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns * @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns
* @return the combined request key * @return a new request key containing conditions from both keys
*/ */
public RequestKey combine(RequestKey methodKey, PathMatcher pathMatcher) { public RequestKey combine(RequestKey methodKey, PathMatcher pathMatcher) {
Set<String> patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher); Set<String> patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher);
@ -199,15 +222,18 @@ public final class RequestKey {
} }
/** /**
* Returns a new {@code RequestKey} that contains all matching attributes of this key, given the {@link * Returns a new {@code RequestKey} that contains all conditions of this key that are relevant to the request.
* HttpServletRequest}. Matching patterns in the returned RequestKey are sorted according to {@link * <ul>
* PathMatcher#getPatternComparator(String)} with the best matching pattern at the top. * <li>The list of URL path patterns is trimmed to contain the patterns that match the URL with matching patterns
* * sorted via {@link PathMatcher#getPatternComparator(String)}.
* @param request the servlet request * <li>The list of HTTP methods is trimmed to contain only the method of the request.
* @param pathMatcher to {@linkplain PathMatcher#match(String, String) match} patterns * <li>Request parameter and request header conditions are included in full.
* @param urlPathHelper to create the {@linkplain UrlPathHelper#getLookupPathForRequest(HttpServletRequest) lookup * <li>The list of consumes conditions is trimmed and sorted to match the request "Content-Type" header.
* path} * </ul>
* @return a new request key that contains all matching attributes * @param request the current request
* @param pathMatcher to check for matching patterns
* @param urlPathHelper to derive the lookup path for the request
* @return a new request key that contains all matching attributes, or {@code null} if not all conditions match
*/ */
public RequestKey getMatchingKey(HttpServletRequest request, PathMatcher pathMatcher, UrlPathHelper urlPathHelper) { public RequestKey getMatchingKey(HttpServletRequest request, PathMatcher pathMatcher, UrlPathHelper urlPathHelper) {
if (!checkMethod(request) || !paramsCondition.match(request) || !headersCondition.match(request) || if (!checkMethod(request) || !paramsCondition.match(request) || !headersCondition.match(request) ||
@ -217,7 +243,7 @@ public final class RequestKey {
else { else {
List<String> matchingPatterns = getMatchingPatterns(request, pathMatcher, urlPathHelper); List<String> matchingPatterns = getMatchingPatterns(request, pathMatcher, urlPathHelper);
if (!matchingPatterns.isEmpty()) { if (!matchingPatterns.isEmpty()) {
Set<RequestMethod> matchingMethods = getMatchingMethods(request); Set<RequestMethod> matchingMethods = getMatchingMethod(request);
return new RequestKey(matchingPatterns, matchingMethods, this.paramsCondition, this.headersCondition, return new RequestKey(matchingPatterns, matchingMethods, this.paramsCondition, this.headersCondition,
this.consumesCondition); this.consumesCondition);
} }
@ -245,7 +271,7 @@ public final class RequestKey {
return matchingPatterns; return matchingPatterns;
} }
private Set<RequestMethod> getMatchingMethods(HttpServletRequest request) { private Set<RequestMethod> getMatchingMethod(HttpServletRequest request) {
if (this.methods.isEmpty()) { if (this.methods.isEmpty()) {
return this.methods; return this.methods;
} }

View File

@ -23,6 +23,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
@ -44,7 +45,22 @@ import org.springframework.web.servlet.handler.MappedInterceptors;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
/** /**
* TODO * An {@link AbstractHandlerMethodMapping} variant that uses {@link RequestKey}s for the registration and the lookup
* of {@link HandlerMethod}s.
*
* <p>A {@link RequestKey} for an incoming request contains the URL and the HTTP method of the request.
* A {@link RequestKey} for a handler method contains all conditions found in the method @{@link RequestMapping}
* annotation combined with all conditions found in the type @{@link RequestMapping} annotation, if present.
*
* <p>An incoming request matches to a handler method directly when a @{@link RequestMapping} annotation contains
* a single, non-pattern URL and a single HTTP method. When a {@link RequestKey} contains additional conditions
* (e.g. more URL patterns, request parameters, headers, etc) those conditions must be checked against the
* request rather than against the key that represents it. This results in the creation of a new handler method
* {@link RequestKey} with the subset of conditions relevant to the current request (see
* {@link RequestKey#getMatchingKey(HttpServletRequest, PathMatcher, UrlPathHelper)}).
* Such keys can then be compared against each other, in the context of the current request, making it possible
* to select to the best matching {@link RequestKey} in case of multiple matches and also the best matching
* pattern within the selected key.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -168,6 +184,18 @@ public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMap
} }
} }
/**
* Returns a new {@link RequestKey} with attributes matching to the current request or {@code null}.
* @see RequestKey#getMatchingKey(HttpServletRequest, PathMatcher, UrlPathHelper)
*/
@Override
protected RequestKey getMatchingKey(RequestKey key, HttpServletRequest request) {
return key.getMatchingKey(request, pathMatcher, urlPathHelper);
}
/**
* Returns a {@link Comparator} that can be used to sort and select the best matching {@link RequestKey}.
*/
@Override @Override
protected Comparator<RequestKey> getKeyComparator(HttpServletRequest request) { protected Comparator<RequestKey> getKeyComparator(HttpServletRequest request) {
return new RequestKeyComparator(request); return new RequestKeyComparator(request);
@ -181,24 +209,10 @@ public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMap
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables); request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
} }
@Override /**
protected RequestKey getMatchingKey(RequestKey key, HttpServletRequest request) { * Iterates all {@link RequestKey}s looking for keys that match by URL but not by HTTP method.
return key.getMatchingKey(request, pathMatcher, urlPathHelper); * @exception HttpRequestMethodNotSupportedException if there are matches by URL but not by HTTP method
} */
@Override
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
if (this.mappedInterceptors != null) {
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
HandlerInterceptor[] handlerInterceptors = mappedInterceptors.getInterceptors(lookupPath, pathMatcher);
if (handlerInterceptors.length > 0) {
chain.addInterceptors(handlerInterceptors);
}
}
return chain;
}
@Override @Override
protected HandlerMethod handleNoMatch(Set<RequestKey> requestKeys, HttpServletRequest request) protected HandlerMethod handleNoMatch(Set<RequestKey> requestKeys, HttpServletRequest request)
throws HttpRequestMethodNotSupportedException { throws HttpRequestMethodNotSupportedException {
@ -223,13 +237,29 @@ public class RequestMappingHandlerMethodMapping extends AbstractHandlerMethodMap
} }
/** /**
* A comparator for RequestKey types. Effective comparison can only be done in the context of a specific request. For * Adds mapped interceptors to the handler execution chain.
* example not all configured patterns may apply to the current request. Therefore an HttpServletRequest is required as */
* input. @Override
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
if (this.mappedInterceptors != null) {
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
HandlerInterceptor[] handlerInterceptors = mappedInterceptors.getInterceptors(lookupPath, pathMatcher);
if (handlerInterceptors.length > 0) {
chain.addInterceptors(handlerInterceptors);
}
}
return chain;
}
/**
* A comparator for {@link RequestKey}s. Effective comparison can only be done in the context of a
* specific request. For example not all {@link RequestKey} patterns may apply to the current request.
* Therefore an HttpServletRequest is required as input.
* *
* Furthermore, the following assumptions are made about the input RequestKeys: <ul> <li>Each RequestKey has been fully * <p>Furthermore, the following assumptions are made about the input RequestKeys:
* matched to the request <li>The RequestKey contains matched patterns only <li>Patterns are ordered with the best * <ul><li>Each RequestKey has been fully matched to the request <li>The RequestKey contains matched
* matching pattern at the top </ul> * patterns only <li>Patterns are ordered with the best matching pattern at the top </ul>
* *
* @see RequestMappingHandlerMethodMapping#getMatchingKey(RequestKey, HttpServletRequest) * @see RequestMappingHandlerMethodMapping#getMatchingKey(RequestKey, HttpServletRequest)
*/ */