AbstractHandlerMapping是實現HandlerMapping接口的一個抽象基類。支持排序,默認處理程序,處理程序攔截器,包括由路徑模式映射的處理程序攔截器。全部的HandlerMapping都繼承自AbstractHandlerMapping。另外,此基類不支持PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE的暴露,此屬性的支持取決於具體的子類,一般基於請求URL映射。web
前面說到,HandlerMapping的做用就是經過request查找Handler和Interceptors。具體的獲取均是經過子類來實現的。spring
1.AbstractHandlerMapping 的類定義
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
AbstractHandlerMapping繼承了WebApplicationObjectSupport,初始化時會自動調用模板方法initApplicationContext;AbstractHandlerMapping的建立也就是在這個方法裏面完成的。同時實現了HandlerMapping和Ordered接口,這也就是上面提到的支持排序的緣由。設計模式
2.AbstractHandlerMapping屬性分析
-
排序值 order跨域
默認值爲Integer的最大值,後面註釋的意思是和沒有排序是同樣的,由於只有理論上纔可能超過Integer.MAX_VALUE。app
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
-
默認處理器 defaultHandlercors
private Object defaultHandler;
-
Spring工具類 urlPathHelperide
Helper類用於URL路徑匹配。提供對RequestDispatcher中URL路徑的支持,包括並支持一致的URL解碼。工具
private UrlPathHelper urlPathHelper = new UrlPathHelper();
-
spring工具類 PathMatcher(AntPathMatcher)學習
用於基於字符串的路徑匹配的策略接口。優化
private PathMatcher pathMatcher = new AntPathMatcher();
-
攔截器列表 interceptors
用於配置SpringMVC的攔截器,配置方式由兩種:<br>
- 1.註冊HandlerMapping時經過屬性設置<br>
- 2.經過子類的extendInterceptors鉤子方法進行設置(extendInterceptors方法是在initApplicationContext中調用的)<br>
interceptors並不會直接使用,二是經過initInterceptors方法按照類型分配到mappedInterceptors和adaptedInterceptors中進行使用,interceptors只用於配置。
private final List<Object> interceptors = new ArrayList<Object>();
-
adaptedInterceptors
被分配到adaptedInterceptors中的類型的攔截器不須要進行匹配,在getHandler中會所有添加到返回值HandlerExecutionChain裏面。他 只能從 interceptors中獲取。
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
-
corsProcessor
CorsProcessor做用是接受請求和CorsConfiguration並更新響應的策略。 此組件不關心如何選擇CorsConfiguration,而是採起後續操做,例如應用CORS驗證檢查,並拒絕響應或將CORS頭添加到響應中。
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
-
corsConfigSource
根據路徑模式上映射的CorsConfiguration集合提供每一個請求的CorsConfiguration實例。支持精確的路徑映射URI(如「/ admin」)以及Ant樣式的路徑模式(如「/ admin / **」)
private final UrlBasedCorsConfigurationSource corsConfigSource = new UrlBasedCorsConfigurationSource();
-
跨域相關問題
CorsConfiguration 具體封裝跨域配置信息的pojo<br> CorsConfigurationSource request與跨域配置信息映射的容器<br> CorsProcessor 具體進行跨域操做的類<br>
3.AbstractHandlerMapping 中的get&set方法
3.1 setOrder
指定此HandlerMapping bean的排序值。
public final void setOrder(int order) { this.order = order; }
3.2 setDefaultHandler
指定此HandlerMapping bean的排序值。 設置此處理程序映射的默認處理程序。 若是沒有找到特定的映射,這個處理程序將被返回。 缺省值爲null,表示沒有默認處理程序。
public void setDefaultHandler(Object defaultHandler) { this.defaultHandler = defaultHandler; }
3.3 getDefaultHandler
返回此處理程序映射的默認處理程序,若是沒有,則返回null。
public Object getDefaultHandler() { return this.defaultHandler; }
3.4 setAlwaysUseFullPath
若是URL查找始終使用當前servlet上下文中的完整路徑,請進行設置。 不然,若是適用,則使用當前servlet映射中的路徑(即,在web.xml中「... / *」servlet映射的狀況下)。 默認是「false」。setAlwaysUseFullPath中的實現具體是委託給urlPathHelper和corsConfigSource來完成的。
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); this.corsConfigSource.setAlwaysUseFullPath(alwaysUseFullPath); }
3.5 setUrlDecode
若是上下文路徑和請求URI應該被URL解碼,則設置。二者都是由Servlet API返回「undecoded」,與servlet路徑相反。根據Servlet規範(ISO-8859-1)使用請求編碼或默認編碼。setUrlDecode中的實現具體是委託給urlPathHelper和corsConfigSource來完成的。
public void setUrlDecode(boolean urlDecode) { this.urlPathHelper.setUrlDecode(urlDecode); this.corsConfigSource.setUrlDecode(urlDecode); }
3.6 setRemoveSemicolonContent
若是「;」 (分號)內容應該從請求URI中去除,則設置。默認值是true。setRemoveSemicolonContent中的實現具體是委託給urlPathHelper和corsConfigSource來完成的。
public void setRemoveSemicolonContent(boolean removeSemicolonContent) { this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent); this.corsConfigSource.setRemoveSemicolonContent(removeSemicolonContent); }
3.7 setUrlPathHelper
設置UrlPathHelper以用於解析查找路徑。 使用它能夠用自定義子類覆蓋默認的UrlPathHelper,或者跨多個HandlerMappings和MethodNameResolvers共享通用的UrlPathHelper設置。
public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); this.urlPathHelper = urlPathHelper; this.corsConfigSource.setUrlPathHelper(urlPathHelper); }
3.8 getUrlPathHelper
返回UrlPathHelper實現以用於解析查找路徑。
public UrlPathHelper getUrlPathHelper() { return urlPathHelper; }
3.9 setPathMatcher
將PathMatcher實現設置爲用於匹配註冊的URL模式的URL路徑。 默認是AntPathMatcher。
public void setPathMatcher(PathMatcher pathMatcher) { Assert.notNull(pathMatcher, "PathMatcher must not be null"); this.pathMatcher = pathMatcher; this.corsConfigSource.setPathMatcher(pathMatcher); }
3.10 setInterceptors
設置攔截器以應用此處理程序映射映射的全部處理程序。 支持的攔截器類型是HandlerInterceptor,WebRequestInterceptor和MappedInterceptor。 映射攔截器僅適用於請求與其路徑模式相匹配的URL。映射的攔截器Bean在初始化期間也會按類型檢測到。
ublic void setInterceptors(Object... interceptors) { this.interceptors.addAll(Arrays.asList(interceptors)); }
其餘幾個get&set方法就不列出來了,有興趣的小夥伴能夠自行閱讀...
4. AbstractHandlerMapping的建立
由於AbstractHandlerMapping繼承了WebApplicationObjectSupport類,所以AbstractHandlerMapping的建立就是依託於模板方法initApplicationContext來完成的。
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
從方法結構能夠了解到,initApplicationContext中包括三個子處理方法。
-
extendInterceptors:這也是一個模板方法,在AbstractHandlerMapping中並無具體實現(方法體是空的),主要是用於給子類提供一個添加(修改)Interceptors的入口(現有的SpringMVC實現中均未使用)。
-
detectMappedInterceptors:用於將SpringMVC容器及父容器中的全部MappedInterceptor類型的Bean添加到MappedInterceptors屬性中。
檢測MappedInterceptor類型的bean,並將它們添加到映射的攔截器列表中。 除了可能經過setInterceptors提供的任何MappedInterceptors以外,還會調用此方法,默認狀況下將從當前上下文及其祖先中添加全部MappedInterceptor類型的Bean。子類能夠覆蓋和優化這個策略。
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); }
- initInterceptors:初始化指定的攔截器,檢查MappedInterceptors並根據須要調整HandlerInterceptors和WebRequestInterceptors。(當前Spring版本時4.3.6)
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"); } this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } }
這個是4.1.5版本的initInterceptors方法:
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"); } if (interceptor instanceof MappedInterceptor) { this.mappedInterceptors.add((MappedInterceptor) interceptor); } else { this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } }
在4.1.5中版本中,initInterceptors的工做是將interceptors屬性裏面所包含的對象按照類型添加到adaptedInterceptors或者mappedInterceptors中。在4.1.5版本中mappedInterceptors是AbstractHandlerMapping的屬性之一。主要緣由是由於,springMVC自4.2開始添加了跨域的支持,也就是上面屬性中的後兩個。PS:在閱讀Spring相關源碼時須要關注不一樣版本的變動及區別,不要只關注某一個版本,另外就是我的以爲閱讀源碼的關注點應該在編碼方式、設計模式使用、設計思想及理念,而不只僅是知道他是如何實現的】
這裏順便說下mappedInterceptors的做用:mappedInterceptors中的攔截器在使用時須要與請求的url進行匹配,只有匹配成功後纔會添加到getHandler的返回值HandlerExecytionChain裏。
adaptInterceptor方法:
使給定的攔截器對象適配HandlerInterceptor接口。默認狀況下,支持的攔截器類型是HandlerInterceptor和WebRequestInterceptor。每一個給定的WebRequestInterceptor將被封裝在WebRequestHandlerInterceptorAdapter中。能夠在子類中重寫。
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()); } }
5.Handler和Interceptor的獲取
HandlerMapping是經過getHandler方法來獲取Handler和Interceptor的。所以在抽象基類AbstractHandlerMapping中提供了具體的實現。而且在AbstractHandlerMapping中,getHandler使用final關鍵字修飾的,也就是說,子類不能再進行對此方法進行覆蓋重寫了。
getHandler的做用就是查找給定請求的handler,若是找不到特定請求,則返回到默認handler。
@Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //經過getHandlerInternal方法來獲取handler Object handler = getHandlerInternal(request); //若是前一個方法沒有獲取到,則使用默認的handler if (handler == null) { //默認的Handler就是AbstractHandlerMapping中的handler屬性經過set獲得的值 handler = getDefaultHandler(); } //若是仍是沒有找到Hander,則直接返回Null if (handler == null) { return null; } // Bean name or resolved handler? //若是找到的handler是String類型的, if (handler instanceof String) { //則以它爲名到spring Mvc的容器中查找相應的Bean String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } //先根據handler和request建立一個HandlerExecutionChain對象, HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
getHandlerInternal:
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
查找給定請求的handler,若是找不到特定請求,則返回null。 這個方法被getHandler調用; 若是設置了null返回值,將致使默認handler。 在CORS pre-flight請求上,這個方法應該返回一個不匹配飛行前請求的匹配項,而是根據URL路徑,「Access-Control-Request-Method」頭中的HTTP方法和頭文件 從「Access-Control-Request-Headers」頭部得到,從而容許CORS配置經過getCorsConfigurations得到, 注意:這個方法也能夠返回一個預先構建的HandlerExecutionChain,將一個處理程序對象與動態肯定的攔截器組合在一塊兒。狀態指定的攔截器將被合併到這個現有的鏈中。
getHandlerExecutionChain:
getLookupPathForRequest:返回給定請求的映射查找路徑,若是適用的話,在當前的servlet映射中,或者在web應用程序中返回。若是在RequestDispatcher中調用include請求,則檢測包含請求URL。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { //若是handler是HandlerExecutionChain類型則直接強轉爲HandlerExecutionChain類型, //若是不是則根據handler建立一個新的HandlerExecutionChain實例對象 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); //返回給定請求的映射查找路徑 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); //遍歷當前adaptedInterceptors鏈表 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { //若是是MappedInterceptor類型則 if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; //攔截器是否應用於給定的請求路徑,若是是則返回true if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
爲給定的handler構建一個HandlerExecutionChain,包括可用的攔截器。默認實現用給定的handler,handler映射的通用攔截器以及與當前請求URL相匹配的任何MappedInterceptors構建標準的HandlerExecutionChain。攔截器按照他們註冊的順序添加。爲了擴展/從新排列攔截器列表,子類能夠覆蓋它。<br>
須要注意的是,傳入的handler對象多是原始handler或預構建的HandlerExecutionChain。這個方法應該明確地處理這兩種狀況,創建一個新的HandlerExecutionChain或者擴展示有的鏈。爲了簡單地在自定義子類中添加攔截器,能夠考慮調用super.getHandlerExecutionChain(handler,request)並在返回的鏈對象上調用HandlerExecutionChain#addInterceptor。
getCorsHandlerExecutionChain:
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request, HandlerExecutionChain chain, CorsConfiguration config) { //經過請求頭的http方法是否options判斷是否預請求, if (CorsUtils.isPreFlightRequest(request)) { HandlerInterceptor[] interceptors = chain.getInterceptors(); //若是是使用PreFlightRequest替換處理器 chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors); } else { //若是是普通請求,添加一個攔截器CorsInterceptor。 chain.addInterceptor(new CorsInterceptor(config)); } return chain; }
更新HandlerExecutionChain進行與CORS(HTTP訪問控制:跨域資源共享)相關的處理。
- 對於pre-flight請求,默認實現用一個簡單的HttpRequestHandler來替換選擇的handler,該HttpRequestHandler調用已配置的setCorsProcessor。(將處理器替換爲內部類PreFlightHandler)
- 對於普通的請求,默認實現插入一個HandlerInterceptor,它執行與CORS有關的檢查並添加CORS頭。(添加CorsInterceptor攔截器)
AbstractHandlerMapping中的兩個內部類
這兩個內部類就是用來校驗request是否cors,並封裝對應的Adapter的。
- PreFlightRequest是CorsProcessor對於HttpRequestHandler的一個適配器。這樣HandlerAdapter直接使用HttpRequestHandlerAdapter處理。
- CorsInterceptor 是CorsProcessor對於HandlerInterceptorAdapter的適配器。
具體的類信息以下:
PreFlightHandler
private class PreFlightHandler implements HttpRequestHandler, CorsConfigurationSource { private final CorsConfiguration config; public PreFlightHandler(CorsConfiguration config) { this.config = config; } @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { corsProcessor.processRequest(this.config, request, response); } @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return this.config; } }
CorsInterceptor
private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource { private final CorsConfiguration config; public CorsInterceptor(CorsConfiguration config) { this.config = config; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return corsProcessor.processRequest(this.config, request, response); } @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return this.config; } }
至此AbstractHandlerMapping中的一些源碼就結束了,AbstractHandlerMapping爲HandlerMapping的功能提供的一些具體的模板描述,可是具體的細節實現還須要從其子類中來慢慢分析。關於這部分中涉及到的如HandlerExecutionChain,cors跨域等問題,後面會根據實際狀況另開篇幅來學習。
你們若是有什麼意見或者建議能夠在下方評論區留言,也能夠給咱們發郵件(glmapper_2018@163.com)!歡迎小夥伴與咱們一塊兒交流,一塊兒成長。