SpringMVC源碼解讀之HandlerMapping
概述
對于Web開發(fā)者,MVC模型是大家再熟悉不過的了,SpringMVC中,滿足條件的請求進(jìn)入到負(fù)責(zé)請求分發(fā)的DispatcherServlet,DispatcherServlet根據(jù)請求url到控制器的映射(HandlerMapping中保存),HandlerMapping最終返回HandlerExecutionChain,其中包含了具體的處理對象handler(也即我們編程時(shí)寫的controller)以及一系列的攔截器interceptors,此時(shí)DispatcherServlet會根據(jù)返回的HandlerExecutionChain中的handler找到支持這一處理器類型的適配器(handlerAdapter),在處理器適配器中最終會去調(diào)用控制器的請求響應(yīng)方法并返回結(jié)果視圖(ModelAndView),得到結(jié)果視圖后,通過render方法完成結(jié)果的顯示。
HanderMapping的繼承體系:
SpringMVC在請求到handler處理器的分發(fā)這步是通過HandlerMapping模塊解決的.handlerMapping 還處理攔截器.
先看看HandlerMapping的繼承樹吧
可以大致這樣做個(gè)分類:
1. 一個(gè)接口HandlerMapping,定義一個(gè)api: HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
2. 一個(gè)基礎(chǔ)抽象類:主要是準(zhǔn)備上下文環(huán)境,提供getHandlerInternal鉤子,封裝攔截器到HandlerExecutionChain
3. 基于注解@Controller,@RequestMapping的使用
4. 配置文件中直接配置url到 handler的SimpleUrlHandlerMapping
5. 默認(rèn)實(shí)現(xiàn)BeanNameUrlHandlerMapping
6. Controller子類的映射
看看HandlerMapping吧,就一個(gè)getHandler api 非常簡單.
// HandlerMapping package org.springframework.web.servlet; public interface HandlerMapping { HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
AbstractHandlerMapping就沒有這么簡單了
先看AbstractHandlerMapping繼承的類,實(shí)現(xiàn)的接口
package org.springframework.web.servlet.handler; public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { // ... }
WebApplicationObjectSupport用于提供上下文ApplicationContext和ServletContext.
還有這邊的initApplicationContext方法,在后續(xù)經(jīng)常會使用到.AbstractHandlerMapping就直接覆寫了.
父類里還是實(shí)現(xiàn)了ApplicationContextAware和ServletContextAware接口,spring概念很統(tǒng)一.
Ordered用于集合排序.
再接著看AbstractHandlerMapping的屬性吧
// AbstractHandlerMapping // order賦了最大值,優(yōu)先級是最小的 private int order = Integer.MAX_VALUE; // default: same as non-Ordered // 默認(rèn)的Handler,這邊使用的Obejct,子類實(shí)現(xiàn)的時(shí)候,使用HandlerMethod,HandlerExecutionChain等 private Object defaultHandler; // url計(jì)算的輔助類 private UrlPathHelper urlPathHelper = new UrlPathHelper(); // 基于ant進(jìn)行path匹配,解決如/books/{id}場景 private PathMatcher pathMatcher = new AntPathMatcher(); // 攔截器配置:,HandlerMapping屬性設(shè)置;,extendInterceptors設(shè)置 private final List<Object> interceptors = new ArrayList<Object>(); // 從interceptors中解析得到,直接添加給全部handler private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>(); // 使用前需要跟url進(jìn)行匹配,匹配通過才會使用 private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
看下攔截器的初始化:
// AbstractHandlerMapping @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); } /** * 提供給子類擴(kuò)展攔截器,可惜都沒有使用 */ protected void extendInterceptors(List<Object> interceptors) { } /** * 掃描應(yīng)用下的MappedInterceptor,并添加到mappedInterceptors */ protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(),MappedInterceptor.class, true, false).values()); } /** * 歸集MappedInterceptor,并適配HandlerInterceptor和WebRequestInterceptor */ protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = ; 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"); } if (interceptor instanceof MappedInterceptor) { mappedInterceptors.add((MappedInterceptor) interceptor); } else { 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()); } }
然后是getHandler(HttpServletRequest request)的實(shí)現(xiàn),這邊同時(shí)預(yù)留getHandlerInternal(HttpServletRequest request)給子類實(shí)現(xiàn)
// AbstractHandlerMapping public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } return getHandlerExecutionChain(handler, request); } protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
最后是封裝攔截器到HandlerExecutionChain
adaptedInterceptors直接添加
mappedInterceptors需要根據(jù)url匹配通過后添加
// AbstractHandlerMapping 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)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } return chain; }
Controller子類的映射,這一分支先看類繼承
我們來說說,這邊每個(gè)類主要的職責(zé)
1. AbstractHandlerMapping 準(zhǔn)備上下文環(huán)境;提供getHandlerInternal鉤子;封裝攔截器到HandlerExecutionChain
2. AbstractUrlHandlerMapping 實(shí)現(xiàn)注冊handler的方法供子類使用;實(shí)現(xiàn)getHandlerInternal,根據(jù)子類初始化的配置信息,查找handler
3. AbstractDetectingUrlHandlerMapping 掃描應(yīng)用下的Object,迭代后提供鉤子方法determineUrlsForHandler由子類決定如何過濾
4. AbstractControllerUrlHandlerMapping 實(shí)現(xiàn)determineUrlsForHandler,添加過濾排除的handler操作(配置文件配置),預(yù)留鉤子方法buildUrlsForHandler給子類實(shí)現(xiàn);同時(shí)判斷controller的子類
5. ControllerBeanNameHandlerMapping 根據(jù)bean name生成url
ControllerClassNameHandlerMapping根據(jù)class name生成url
從AbstractUrlHandlerMapping開始看吧,這邊只是大致看下代碼,如果需要仔細(xì)分析,請移步<SpringMVC源碼解讀 - HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā)>
handler的注冊
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { } protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { }
handler的查找
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {} // 根據(jù)url查找handler protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {} // 校驗(yàn)handler protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {} // 封裝攔截器到HandlerExecutionChain protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, Map<String, String> uriTemplateVariables) {}
AbstractDetectingUrlHandlerMapping,這邊一樣不展開,具體移步<SpringMVC源碼解讀 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>
具體做的事情:
1. 通過覆寫initApplicationContext,調(diào)用detectHandlers掃描Obejct
2. 提供鉤子方法determineUrlsForHandler給子類根據(jù)handler生成url
3. 調(diào)用父類的registerHandler進(jìn)行注冊
@Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); detectHandlers(); } protected void detectHandlers() throws BeansException { // ... } /** * Determine the URLs for the given handler bean. * 鉤子而已 */ protected abstract String[] determineUrlsForHandler(String beanName); AbstractControllerUrlHandlerMapping,這邊一樣不展開,具體移步<SpringMVC源碼解讀 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>
具體做的事情;
1. 覆寫determineUrlsForHandler添加剔除部分類的邏輯,通過配置文件配置的excludedClasses和excludedPackages在這邊使用
2. 判斷是否controller的子類
3. 預(yù)留buildUrlsForHandler給子類生成url
@Override protected String[] determineUrlsForHandler(String beanName) { Class beanClass = getApplicationContext().getType(beanName); if (isEligibleForMapping(beanName, beanClass)) { return buildUrlsForHandler(beanName, beanClass); } else { return null; } } protected boolean isEligibleForMapping(String beanName, Class beanClass) {} protected boolean isControllerType(Class beanClass) {} protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass); ControllerBeanNameHandlerMapping和ControllerClassNameHandlerMapping 直接看源碼吧,或者移步<SpringMVC源碼解讀 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>
配置文件中直接配置url到 handler的SimpleUrlHandlerMapping,就是使用registerHandlers注冊配置文檔中的handler,直接看代碼或者移步<SpringMVC源碼解讀 - HandlerMapping - SimpleUrlHandlerMapping初始化>吧
BeanNameUrlHandlerMapping 實(shí)現(xiàn)determineUrlsForHandler生成url,直接看代碼或者移步<SpringMVC源碼解讀 - HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化>吧
基于注解@Controller,@RequestMapping的使用
最難吭的骨頭
先看類繼承吧
說下各個(gè)類的職責(zé)吧,具體的分析還是移步下面的文章
<SpringMVC源碼解讀 - HandlerMapping - RequestMappingHandlerMapping初始化>
<SpringMVC源碼解讀 - HandlerMapping - RequestMappingHandlerMapping請求分發(fā)>
1. AbstractHandlerMethodMaping 定義初始化流程,請求時(shí)如何映射
初始化:
1.1.1 掃描應(yīng)用下的Object
1.1.2 預(yù)留isHandler鉤子方法給子類判斷Object是否handler
1.1.3 迭代掃描每一個(gè)handler,找出符合要求的方法,這邊判斷依然是留給子類實(shí)現(xiàn)getMappingForMethod
1.1.4 注冊查找到的處理器,需要確保一個(gè)匹配條件RequestMappingInfo只能映射到一個(gè)handler
1.1.5 根據(jù)匹配條件獲取url,同樣的只是定義流程,具體的算法留給子類實(shí)現(xiàn)getMappingPathPatterns
請求request分發(fā)處理:
1.2.1 直接字符串匹配的方式,查找handler
1.2.2 匹配條件查找,這邊具體的算法交由子類處理getMatchingMapping
1.2.3 排序并獲取最佳匹配handler,這邊的排序方式還是子類處理getMappingConmparator
1.2.4 分別封裝匹配到和未匹配到handler的情況
2. RequestMappingInfoHandlerMapping使用RequestMappingInfo實(shí)現(xiàn)匹配條件,RequestMappingInfo的初始化留給子類
2.1 根據(jù)RequestMappingInfo生成url ->getMappingPathPatterns
2.2 使用匹配條件查找Handler -> getMatchingMapping
2.3 完成比較器算法 -> getMappingComparator
2.4 覆寫handleMatch,緩存n多信息到request
注冊pattern,最佳匹配的pattern,url中解析出來的參數(shù),url中解析出來的多值參數(shù),mediaType
2.1.5 覆寫handlerNoMatch,最后的掙扎,再嘗試匹配一次
3. RequestMappingHandlerMapping 根據(jù)注解@Controller @RequestMapping生成RequestMappingInfo,并校驗(yàn)isHandler
3.1 覆寫afterPropertiesSet,添加文件后綴判斷
3.2 實(shí)現(xiàn)isHandler,類上有@Controller @RequestMapping其中一個(gè)注解就對
3.3 解析注解內(nèi)容,生產(chǎn)RequestMappingInfo實(shí)例
相關(guān)文章
java通過Callable和Future來接收線程池的執(zhí)行結(jié)果
這篇文章主要介紹了java通過Callable和Future來接收線程池的執(zhí)行結(jié)果,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08SpringSecurity?Web權(quán)限方案實(shí)現(xiàn)全過程
Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和授權(quán)框架,專門用于保護(hù)Java應(yīng)用程序的Web集成,下面這篇文章主要給大家介紹了關(guān)于SpringSecurity?Web權(quán)限方案實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-01-01seata-1.4.0安裝及在springcloud中使用詳解
這篇文章主要介紹了seata-1.4.0安裝及在springcloud中使用,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12spring boot整合log4j2及MQ消費(fèi)處理系統(tǒng)日志示例
這篇文章主要為大家介紹了spring boot整合log4j2及MQ消費(fèi)處理系統(tǒng)日志的示例過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03Spring使用AspectJ注解和XML配置實(shí)現(xiàn)AOP
這篇文章主要介紹了Spring使用AspectJ注解和XML配置實(shí)現(xiàn)AOP的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Java數(shù)據(jù)結(jié)構(gòu)之二叉查找樹的實(shí)現(xiàn)
二叉查找樹(亦稱二叉搜索樹、二叉排序樹)是一棵二叉樹,且各結(jié)點(diǎn)關(guān)鍵詞互異,其中根序列按其關(guān)鍵詞遞增排列。本文將通過示例詳細(xì)講解二叉查找樹,感興趣的可以了解一下2022-03-03