SpringMVCソース解読-HandlerMapping-RequestMappingHandlerMappingリクエスト配信
32358 ワード
AbstractHandlerMethodMapping実装インタフェースgetHandlerInternal,定義検索プロセス
RequestMappingInfoHandlerMapping RequestMappingInfoに従ってマッチング条件を細分化し、マッチングができない場合はRequestConditionを使用してマッチングを繰り返します
RequestMappingHandlerMappingは受益者ですが、こちらでは何もしていません(初期化時に@Controller,@RequestMapping注記に基づいてRequestMappingInfoを生成し、この2つの注記に基づいてターゲットHandlerがisHandlerを実現するか否かを判断します)
AbstractHandlerMethodMapping実装インタフェースgetHandlerInternal
1. UrlPathHelperを使用してrequest対応pathを検索
2. パスに対応するHandlerMethodを検索
2.1 urlMapからの直接等値照合照合照合照合条件RequestMappingInfoの検索
2.2等値が一致条件を見つけたらmatch条件に追加する
2.3一致条件が見つからない場合、すべてのhandlerMethodのRequestMappingInfoを使用して一致させる
2.4マッチしたMatchを並べ替え、最優先度のMatchを取り出し、唯一の最優先度かどうかを確認する
2.5条件にマッチし、条件にマッチしていない2つのケースをそれぞれカプセル化する
3. HandlerMethodをカプセル化し、beanにインスタンスが保存されていることを確認します.
// AbstractHandlerMethodMapping
インタフェースgetHandlerInternalの実装
// AbstractHandlerMethodMapping
// AbstractHandlerMethodMapping
条件を満たすRequestConditionの特定の検索
// AbstractHandlerMethodMapping
RequestMappingInfoHandlerMappingの実装を見てみましょう.RequestMappingInfoから該当するRequestConditionを検索します.
// RequestMappingInfoHandlerMapping
//AbstractHandlerMethodMapping
RequestMappingInfoHandlerMappingでは、何に使うのか、HandlerAdaptorを見てからにしましょう.
RequestMappingInfoHandlerMapping
// RequestMappingInfoHandlerMapping
RequestMappingInfoHandlerMapping RequestMappingInfoに従ってマッチング条件を細分化し、マッチングができない場合はRequestConditionを使用してマッチングを繰り返します
RequestMappingHandlerMappingは受益者ですが、こちらでは何もしていません(初期化時に@Controller,@RequestMapping注記に基づいてRequestMappingInfoを生成し、この2つの注記に基づいてターゲットHandlerがisHandlerを実現するか否かを判断します)
AbstractHandlerMethodMapping実装インタフェースgetHandlerInternal
1. UrlPathHelperを使用してrequest対応pathを検索
2. パスに対応するHandlerMethodを検索
2.1 urlMapからの直接等値照合照合照合照合条件RequestMappingInfoの検索
2.2等値が一致条件を見つけたらmatch条件に追加する
2.3一致条件が見つからない場合、すべてのhandlerMethodのRequestMappingInfoを使用して一致させる
2.4マッチしたMatchを並べ替え、最優先度のMatchを取り出し、唯一の最優先度かどうかを確認する
2.5条件にマッチし、条件にマッチしていない2つのケースをそれぞれカプセル化する
3. HandlerMethodをカプセル化し、beanにインスタンスが保存されていることを確認します.
// AbstractHandlerMethodMapping
インタフェースgetHandlerInternalの実装
1 package org.springframework.web.servlet.handler
2 // AbstractHandlerMethodMapping<T>
3 /**
4 * Look up a handler method for the given request.
5 */
6 @Override
7 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
8 // request url
9 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
10 // , HandlerMethod
11 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
12 // bean
13 return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
14 }
// AbstractHandlerMethodMapping
package org.springframework.web.servlet.handler;
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// urlMap RequestMappingInfo
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
//
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings
// , handlerMethods
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
// Match
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
}
// request url ,mediaType , RequestMappingHandlerMapping
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
// RequestMappingHandlerMapping
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
}
// AbstractHandlerMethodMapping
条件を満たすRequestConditionの特定の検索
1 package org.springframework.web.servlet.handler;
2 public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
3
4 private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
5 for (T mapping : mappings) {
6 T match = getMatchingMapping(mapping, request);
7 if (match != null) {
8 matches.add(new Match(match, handlerMethods.get(mapping)));
9 }
10 }
11 }
// AbstractHandlerMethodMapping
1 /**
2 * Check if a mapping matches the current request and return a (potentially
3 * new) mapping with conditions relevant to the current request.
4 * @param mapping the mapping to get a match for
5 * @param request the current HTTP servlet request
6 * @return the match, or {@code null} if the mapping doesn't match
7 */
8 protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
RequestMappingInfoHandlerMappingの実装を見てみましょう.RequestMappingInfoから該当するRequestConditionを検索します.
// RequestMappingInfoHandlerMapping
1 /**
2 * Check if the given RequestMappingInfo matches the current request and
3 * return a (potentially new) instance with conditions that match the
4 * current request -- for example with a subset of URL patterns.
5 * @return an info in case of a match; or {@code null} otherwise.
6 */
7 @Override
8 protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
9 return info.getMatchingCondition(request);
10 }
//AbstractHandlerMethodMapping
1 /**
2 * Invoked when a matching mapping is found.
3 * @param mapping the matching mapping
4 * @param lookupPath mapping lookup path within the current servlet mapping
5 * @param request the current request
6 */
7 protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
8 request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
9 }
RequestMappingInfoHandlerMappingでは、何に使うのか、HandlerAdaptorを見てからにしましょう.
1 /**
2 * Expose URI template variables, matrix variables, and producible media types in the request.
3 * @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
4 * @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
5 * @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
6 */
7 @Override
8 protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
9 super.handleMatch(info, lookupPath, request);
10
11 Set<String> patterns = info.getPatternsCondition().getPatterns();
12 String bestPattern = patterns.isEmpty() ? lookupPath : patterns.iterator().next();
13 request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
14
15 Map<String, String> uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
16 Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
17 request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
18
19 if (isMatrixVariableContentAvailable()) {
20 request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(request, uriVariables));
21 }
22
23 if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
24 Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
25 request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
26 }
27 }
1 /**
2 * Invoked when no matching mapping is not found.
3 * @param mappings all registered mappings
4 * @param lookupPath mapping lookup path within the current servlet mapping
5 * @param request the current request
6 * @throws ServletException in case of errors
7 */
8 protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
9 throws Exception {
10
11 return null;
12 }
RequestMappingInfoHandlerMapping
// RequestMappingInfoHandlerMapping
1 /**
2 * Iterate all RequestMappingInfos once again, look if any match by URL at
3 * least and raise exceptions accordingly.
4 * @throws HttpRequestMethodNotSupportedException if there are matches by URL
5 * but not by HTTP method
6 * @throws HttpMediaTypeNotAcceptableException if there are matches by URL
7 * but not by consumable/producible media types
8 */
9 @Override
10 protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos,
11 String lookupPath, HttpServletRequest request) throws ServletException {
12
13 Set<String> allowedMethods = new LinkedHashSet<String>(4);
14
15 Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>();
16 Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>();
17
18 for (RequestMappingInfo info : requestMappingInfos) {
19 if (info.getPatternsCondition().getMatchingCondition(request) != null) {
20 patternMatches.add(info);
21 if (info.getMethodsCondition().getMatchingCondition(request) != null) {
22 patternAndMethodMatches.add(info);
23 }
24 else {
25 for (RequestMethod method : info.getMethodsCondition().getMethods()) {
26 allowedMethods.add(method.name());
27 }
28 }
29 }
30 }
31
32 if (patternMatches.isEmpty()) {
33 return null;
34 }
35 else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) {
36 throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
37 }
38
39 Set<MediaType> consumableMediaTypes;
40 Set<MediaType> producibleMediaTypes;
41 Set<String> paramConditions;
42
43 if (patternAndMethodMatches.isEmpty()) {
44 consumableMediaTypes = getConsumableMediaTypes(request, patternMatches);
45 producibleMediaTypes = getProducibleMediaTypes(request, patternMatches);
46 paramConditions = getRequestParams(request, patternMatches);
47 }
48 else {
49 consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches);
50 producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches);
51 paramConditions = getRequestParams(request, patternAndMethodMatches);
52 }
53
54 if (!consumableMediaTypes.isEmpty()) {
55 MediaType contentType = null;
56 if (StringUtils.hasLength(request.getContentType())) {
57 try {
58 contentType = MediaType.parseMediaType(request.getContentType());
59 }
60 catch (IllegalArgumentException ex) {
61 throw new HttpMediaTypeNotSupportedException(ex.getMessage());
62 }
63 }
64 throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
65 }
66 else if (!producibleMediaTypes.isEmpty()) {
67 throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
68 }
69 else if (!CollectionUtils.isEmpty(paramConditions)) {
70 String[] params = paramConditions.toArray(new String[paramConditions.size()]);
71 throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap());
72 }
73 else {
74 return null;
75 }
76 }