該系列文檔是本人在學(xué)習 Spring MVC 的源碼過(guò)程中總結下來(lái)的,可能對讀者不太友好,請結合我的源碼注釋 Spring MVC 源碼分析 GitHub 地址 進(jìn)行閱讀
Spring 版本:5.1.14.RELEASE
該系列其他文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》
HandlerMapping 組件,請求的處理器匹配器,負責為請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors)
handler 處理器是 Object 類(lèi)型,可以將其理解成 HandlerMethod 對象(例如我們使用最多的 @RequestMapping 注解所標注的方法會(huì )解析成該對象),包含了方法的所有信息,通過(guò)該對象能夠執行該方法
HandlerInterceptor 攔截器對處理請求進(jìn)行增強處理,可用于在執行方法前、成功執行方法后、處理完成后進(jìn)行一些邏輯處理
由于 HandlerMapping 組件涉及到的內容比較多,考慮到內容的排版,所以將這部分內容拆分成了四個(gè)模塊,依次進(jìn)行分析:
先來(lái)回顧一下在 DispatcherServlet 中處理請求的過(guò)程中哪里使用到 HandlerMapping 組件,可以回到《一個(gè)請求的旅行過(guò)程》中的 DispatcherServlet 的 doDispatch 方法中看看,如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; // ... 省略相關(guān)代碼 // Determine handler for the current request. // <3> 獲得請求對應的 HandlerExecutionChain 對象(HandlerMethod 和 HandlerInterceptor 攔截器們) mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { // <3.1> 如果獲取不到,則根據配置拋出異?;蚍祷?404 錯誤 noHandlerFound(processedRequest, response); return; } // ... 省略相關(guān)代碼}@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 遍歷 handlerMappings 組件們 for (HandlerMapping mapping : this.handlerMappings) { // 通過(guò) HandlerMapping 組件獲取到 HandlerExecutionChain 對象 HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { // 不為空則直接返回 return handler; } } } return null;}通過(guò)遍歷 HandlerMapping 組件們,根據請求獲取到對應 HandlerExecutionChain 處理器執行鏈。注意,這里是通過(guò)一個(gè)一個(gè)的 HandlerMapping 組件去進(jìn)行處理,如果找到對應 HandlerExecutionChain 對象則直接返回,不會(huì )繼續下去,所以初始化的 HandlerMapping 組件是有一定的先后順序的,默認是BeanNameUrlHandlerMapping -> RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerMapping 接口,請求的處理器匹配器,負責為請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors),代碼如下:
public interface HandlerMapping {String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";/** * 獲得請求對應的處理器和攔截器們 */@NullableHandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;}HandlerMapping 接口體系的結構如下:

藍色框 AbstractHandlerMapping 抽象類(lèi),實(shí)現了“為請求找到合適的 HandlerExecutionChain 處理器執行鏈”對應的的骨架邏輯,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子類(lèi)實(shí)現。
AbstractHandlerMapping 的子類(lèi),分成兩派,分別是:
@RequestMapping 等注解的方式所取代。不過(guò),Spring MVC 內置的一些路徑匹配,還是使用這種方式。@RequestMapping 等注解的方式。綠色框的 MatchableHandlerMapping 接口,定義了“判斷請求和指定 pattern 路徑是否匹配”的方法。
在 DispatcherServlet 的 initHandlerMappings(ApplicationContext context) 方法,會(huì )在 onRefresh 方法被調用,初始化 HandlerMapping 組件,方法如下:
private void initHandlerMappings(ApplicationContext context) { // 置空 handlerMappings this.handlerMappings = null; // <1> 如果開(kāi)啟探測功能,則掃描已注冊的 HandlerMapping 的 Bean 們,添加到 handlerMappings 中 if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. // 掃描已注冊的 HandlerMapping 的 Bean 們 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); // 添加到 handlerMappings 中,并進(jìn)行排序 if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } // <2> 如果關(guān)閉探測功能,則獲得 Bean 名稱(chēng)為 "handlerMapping" 對應的 Bean ,將其添加至 handlerMappings else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. /** * <3> 如果未獲得到,則獲得默認配置的 HandlerMapping 類(lèi) * {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping} * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping} */ if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } }}如果“開(kāi)啟”探測功能,則掃描已注冊的 HandlerMapping 的 Bean 們,添加到 handlerMappings 中,默認開(kāi)啟
如果“關(guān)閉”探測功能,則獲得 Bean 名稱(chēng)為 "handlerMapping" 對應的 Bean ,將其添加至 handlerMappings
如果未獲得到,則獲得默認配置的 HandlerMapping 類(lèi),調用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) 方法,就是從 DispatcherServlet.properties 文件中讀取 HandlerMapping 的默認實(shí)現類(lèi),如下:
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping可以看到對應的是 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping 對象
org.springframework.web.servlet.handler.AbstractHandlerMapping,實(shí)現 HandlerMapping、Ordered、BeanNameAware 接口,繼承 WebApplicationObjectSupport 抽象類(lèi)
該類(lèi)是 HandlerMapping 接口的抽象基類(lèi),實(shí)現了“為請求找到合適的 HandlerExecutionChain 處理器執行鏈”對應的的骨架邏輯,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子類(lèi)實(shí)現
WebApplicationObjectSupport 抽象類(lèi),提供 applicationContext 屬性的聲明和注入。
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {/** * 默認處理器 */@Nullableprivate Object defaultHandler;/** * URL 路徑工具類(lèi) */private UrlPathHelper urlPathHelper = new UrlPathHelper();/** * 路徑匹配器 */private PathMatcher pathMatcher = new AntPathMatcher();/** * 配置的攔截器數組. * * 在 {@link #initInterceptors()} 方法中,初始化到 {@link #adaptedInterceptors} 中 * * 添加方式有兩種: * 1. {@link #setInterceptors(Object...)} 方法 * 2. {@link #extendInterceptors(List)} 方法 */private final List<Object> interceptors = new ArrayList<>();/** * 初始化后的攔截器 HandlerInterceptor 數組 */private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();private CorsProcessor corsProcessor = new DefaultCorsProcessor();private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered/** * 當前 Bean 的名稱(chēng) */@Nullableprivate String beanName; // ... 省略相關(guān) getter、setter 方法}defaultHandler:默認處理器,在獲得不到處理器時(shí),可使用該屬性
interceptors:配置的攔截器數組
adaptedInterceptors:初始化后的攔截器 HandlerInterceptor 數組,也就是interceptors 轉換成的 HandlerInterceptor 攔截器對象
initApplicationContext()方法,用于初始化攔截器們,方法如下:
在父類(lèi) WebApplicationObjectSupport 的父類(lèi) ApplicationObjectSupport 中可以看到,因為實(shí)現了 ApplicationContextAware 接口,則在初始化該 Bean 的時(shí)候會(huì )調用
setApplicationContext(@Nullable ApplicationContext context)方法,在這個(gè)方法中會(huì )調用initApplicationContext()這個(gè)方法
@Overrideprotected void initApplicationContext() throws BeansException { // <1> 空實(shí)現,交給子類(lèi)實(shí)現,用于注冊自定義的攔截器到 interceptors 中,目前暫無(wú)子類(lèi)實(shí)現 extendInterceptors(this.interceptors); // <2> 掃描已注冊的 MappedInterceptor 的 Bean 們,添加到 mappedInterceptors 中 detectMappedInterceptors(this.adaptedInterceptors); // <3> 將 interceptors 初始化成 HandlerInterceptor 類(lèi)型,添加到 mappedInterceptors 中 initInterceptors();}調用 extendInterceptors(List<Object> interceptors) 方法,空方法,目前暫無(wú)子類(lèi)實(shí)現,暫時(shí)忽略
調用 detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) 方法,從 Spring 的上下文中,掃描已注冊的 MappedInterceptor 的攔截器們,添加到 adaptedInterceptors 中,方法如下:
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { // 掃描已注冊的 MappedInterceptor 的 Bean 們,添加到 mappedInterceptors 中 // MappedInterceptor 會(huì )根據請求路徑做匹配,是否進(jìn)行攔截 mappedInterceptors.addAll(BeanFactoryUtils .beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false) .values());}調用 initInterceptors() 方法,將 interceptors 初始化成 HandlerInterceptor 類(lèi)型,添加到 adaptedInterceptors 中,方法如下:
protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } // 將 interceptors 初始化成 HandlerInterceptor 類(lèi)型,添加到 mappedInterceptors 中 // 注意,HandlerInterceptor 無(wú)需進(jìn)行路徑匹配,直接攔截全部 this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } }}protected HandlerInterceptor adaptInterceptor(Object interceptor) { if (interceptor instanceof HandlerInterceptor) { return (HandlerInterceptor) interceptor; } else if (interceptor instanceof WebRequestInterceptor) { return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); } else { throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); }}關(guān)于攔截器在后文進(jìn)行分析
getHandler(HttpServletRequest request) 方法,獲得請求對應的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors),方法如下:
@Override@Nullablepublic final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // <1> 獲得處理器(HandlerMethod 或者 HandlerExecutionChain),該方法是抽象方法,由子類(lèi)實(shí)現 Object handler = getHandlerInternal(request); // <2> 獲得不到,則使用默認處理器 if (handler == null) { handler = getDefaultHandler(); } // <3> 還是獲得不到,則返回 null if (handler == null) { return null; } // Bean name or resolved handler? // <4> 如果找到的處理器是 String 類(lèi)型,則從 Spring 容器中找到對應的 Bean 作為處理器 if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // <5> 創(chuàng )建 HandlerExecutionChain 對象(包含處理器和攔截器) HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain;}調用getHandlerInternal(HttpServletRequest request) 抽象方法,獲得 handler 處理器
如果 handler 處理器沒(méi)有找到,則調用getDefaultHandler() 方法,使用默認處理器,也就是 defaultHandler 屬性
如果 handler 處理器沒(méi)有找到,且沒(méi)有默認的處理器,則直接返回 null
如果找到的處理器是 String 類(lèi)型,可能是 Bean 的名稱(chēng),則從 Spring 容器中找到對應的 Bean 作為處理器
調用 getHandlerExecutionChain(Object handler, HttpServletRequest request) 方法,獲得 HandlerExecutionChain 對象,方法如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { // <1> 創(chuàng )建 HandlerExecutionChain 對象 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); // <2> 獲得請求路徑 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); // <3> 遍歷 adaptedInterceptors 數組,獲得請求匹配的攔截器 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { // 需要匹配,若路徑匹配,則添加到 chain 中 if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { // 匹配 chain.addInterceptor(mappedInterceptor.getInterceptor()); } } // 無(wú)需匹配,直接添加到 chain 中 else { chain.addInterceptor(interceptor); } } return chain}handler 處理器就是該類(lèi)型對象,則直接使用adaptedInterceptors 攔截器數組,根據請求路徑獲得當前請求匹配的攔截器們,添加到 HandlerExecutionChain 對象中返回上面創(chuàng )建的 HandlerExecutionChain 對象
org.springframework.web.servlet.handler.MatchableHandlerMapping,定義了“判斷請求和指定 pattern 路徑是否匹配”的方法。代碼如下:
public interface MatchableHandlerMapping extends HandlerMapping {/** * 判斷請求和指定 pattern 路徑是否匹配 */@NullableRequestMatchResult match(HttpServletRequest request, String pattern);}org.springframework.web.servlet.handler.RequestMatchResult 類(lèi),判斷請求和指定 pattern 路徑是否匹配時(shí),返回的匹配結果,代碼如下:
public class RequestMatchResult {/** * 匹配到的路徑 */private final String matchingPattern;/** * 被匹配的路徑 */private final String lookupPath;/** * 路徑匹配器 */private final PathMatcher pathMatcher;public RequestMatchResult(String matchingPattern, String lookupPath, PathMatcher pathMatcher) {Assert.hasText(matchingPattern, "'matchingPattern' is required");Assert.hasText(lookupPath, "'lookupPath' is required");Assert.notNull(pathMatcher, "'pathMatcher' is required");this.matchingPattern = matchingPattern;this.lookupPath = lookupPath;this.pathMatcher = pathMatcher;}public Map<String, String> extractUriTemplateVariables() {return this.pathMatcher.extractUriTemplateVariables(this.matchingPattern, this.lookupPath);}}目前實(shí)現 MatchableHandlerMapping 接口的類(lèi),有 RequestMappingHandlerMapping 類(lèi)和 AbstractUrlHandlerMapping 抽象類(lèi),在后續都會(huì )進(jìn)行分析
本文對 Spring MVC 處理請求的過(guò)程中使用到的 HandlerMapping 組件進(jìn)行了分析,會(huì )為請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors)
HandlerMapping 組件的實(shí)現類(lèi)分為兩種:
@RequestMapping 等注解的方式所取代。不過(guò),Spring MVC 內置的一些路徑匹配,還是使用這種方式@RequestMapping 等注解的方式AbstractHandlerMapping 抽象類(lèi),作為一個(gè)基類(lèi),實(shí)現了“為請求找到合適的 HandlerExecutionChain 處理器執行鏈”對應的的骨架邏輯,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子類(lèi)實(shí)現。
本文對 HandlerMapping 組件做了一個(gè)簡(jiǎn)單的介紹,更多的細節交由其子類(lèi)去實(shí)現,由于涉及到的內容比較多,BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping 兩種實(shí)現類(lèi)則在后續的文檔中依次進(jìn)行分析
參考文章:芋道源碼《精盡 Spring MVC 源碼分析》
聯(lián)系客服