Spring MVC 的請求分發核心在於 HandlerMapping 接口及其實現類,負責將 HTTP 請求映射到對應的處理器,並構建包含攔截器的 HandlerExecutionChain。本文基於源碼深入剖析這一流程。
AbstractHandlerMapping 實現 HandlerMapping,定義最終對外暴露的 getHandler() 方法,採用模板方法模式:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request); // 1. 子類實現具體查找邏輯
if (handler == null) {
handler = getDefaultHandler(); // 2. 兜底預設處理器
}
if (handler == null) return null;
if (handler instanceof String) { // 3. Bean 名稱轉實例
handler = getApplicationContext().getBean((String) handler);
}
return getHandlerExecutionChain(handler, request); // 4. 封裝攔截器鏈
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain)
? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
chain.addInterceptors(getAdaptedInterceptors()); // 全局攔截器
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, pathMatcher)) { // 路徑匹配的 MappedInterceptor
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
繼承自 AbstractHandlerMapping,實現 getHandlerInternal():
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request); // 核心匹配邏輯
// ... 預設處理器兜底、路徑暴露屬性等
return handler;
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 1. 精確匹配
Object handler = this.handlerMap.get(urlPath);
if (handler != null) return buildPathExposingHandler(handler, urlPath, urlPath, null);
// 2. 模式匹配
List<String> matchingPatterns = new ArrayList<>();
for (String pattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(pattern, urlPath)) matchingPatterns.add(pattern);
}
if (!matchingPatterns.isEmpty()) {
// 按優先級排序(最長路徑優先)
Collections.sort(matchingPatterns, getPathMatcher().getPatternComparator(urlPath));
String bestMatch = matchingPatterns.get(0);
handler = this.handlerMap.get(bestMatch);
// 提取 URI 模板變數
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
Map<String, String> uriTemplateVars = new LinkedHashMap<>();
for (String p : matchingPatterns) {
if (getPathMatcher().getPatternComparator(urlPath).compare(bestMatch, p) == 0) {
uriTemplateVars.putAll(getUrlPathHelper().decodePathVariables(request,
getPathMatcher().extractUriTemplateVariables(p, urlPath)));
}
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVars);
}
return null;
}
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
此方法將最佳匹配路徑、路徑內相對路徑、URI 模板變數通過攔截器暴露給後續處理器使用。
protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
// 預留給子類覆寫,預設為空實現
}