1、 飛機web
最近馬來西亞航空370號班機事故鬧得沸沸揚揚,情節整的撲朔迷離,連我在鑽研springMVC平和的心情都間接的受到了影響。正當我在想這個MVC的處理過程能夠怎樣獲得更好的理解呢?灰機,灰機… 同事的議論和天上的嗡嗡聲剎那間給了我一個答案,能夠和民航系統進行類比。spring
民航系統相似於一個專業處理旅行的系統,此係統中有一些對旅行去作的事好比出差等自己意義不大,只在旅行系統中才存在的術語。好比說安全
1. 航班MH370是一個航班代號,表示從吉隆坡到北京的航班;session
2. 航班時刻表來進行記錄,查找全部的飛行航班;mvc
3. 機票:在民航系統中惟一標識旅行者的token,上飛機須要飛機票,下飛機憑飛機票取行李;app
4. 安檢:檢查一下有沒有帶違禁品;工具
5. 飛機:完成從出發地到目的地的旅行;post
6. 機場:從乘客來看,無非是機票發售,安全檢查,提供航班的地方。url
回到web系統,一個url就是惟一標識一個處理邏輯的名稱。好比login.do就等於登錄邏輯的標識。HandlerMapping至關於航班時刻表,標識航班和對應的路線。HandlerMethod至關於處理航班的飛機,來完成從登機處到目的地的轉換。handlerAdapter 至關於從平常狀態處和飛行狀態轉換的適配工具,相似於機場。HandlerInterceptor至關於在這個轉換過程當中的共性處理邏輯,如購買機票,上飛機前要去憑身份證購買機票,到了目的地還要憑機票取行李等航班旅行通用的事項。spa
這四大組件構成了spring mvc 這個處理的基石。如圖所示:
圖1:四大組件
顯然,當進行旅行,須要完成從普通狀態到旅行狀態的適配,如將身份證和錢轉換成機票(在旅行中惟一標識本身身份的token) ,而後還要將行李存到飛機上;到了目的地機場後,就要進行旅行狀態到普通狀態的適配,使用機票進行取行李的操做。全部的登機過程都要經歷一些通用邏輯的處理,如進行安檢,進行行李託運/取回。這部分就構成了HandlerInterceptor的存在理由。
這四大組件充分的將web環境和業務邏輯環境解耦,使得業務邏輯能夠更關注業務自己,並將橫向的旅行邏輯和縱向的通用邏輯如安檢進行切割。這四大組件自己都是可擴展的,HandlerMethod最通用,爲方法級別的抽象。HandlerInteceptor是縱向邏輯的擴展接口,HandlerAdapter是從web環境到業務環境的適配擴展接口,HandlerMapping是將URL請求到業務邏輯映射的擴展接口。
1、 組件結構
HandlerMapping
HanderAdapter
3、 處理過程:
邏輯主要集中在org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse)
A.根據請求(如url,參數,head等)特徵來獲得相應的方法和其匹配的攔截器集合。Spring mvc 3.0可用註解來標識處理邏輯方法和URL映射關係,所以涉及到掃描源代碼標識爲@Controller的類,並從中尋找@RequestMapping來進行映射表的生成。
mappedHandler = getHandler(processedRequest, false);
1. org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(HttpServletRequest)
獲取請求路徑lookupPath去尋找相應的handlerMethod方法
1.1 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(String, HttpServletRequest)
1.1.1 先將全部的映射按照requestURI來分組並存儲在urlMap中。urlMap中根據請求路徑找到對應的匹配列表,以進行進一步匹配。匹配列表中除請求的requestURI相同外,其餘某些特徵如方法,POST,GET,請求的head是不一樣的。
1.1.2 org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getMatchingMapping(RequestMappingInfo, HttpServletRequest)
先從請求中找到匹配條件,如method,params,produce,consume等條件,知足產生match
並對這些match的匹配程度進行排序,獲得最匹配的match
2.getHandlerExecutionChain(handler, request)
將匹配的Interceptor也加入,並返回
B.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
適配處理方法如何適配,會從request中獲得相應的parameter,session,Model等等,來支持自定義Controller的參數。
1. org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(HttpServletRequest, HttpServletResponse, Object) 進行適配
1.1 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod)
1.2 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod)
1.2.1數據綁定方法和controller對應
適配主要包含兩部分,調用參數的適配和返回結果的適配
C.HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
獲得全部的interceptors而且將interceptors.preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)進行逐個調用。
D.進行調用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
E.全部的interceptors進行postHandle調用
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
F.org.springframework.web.servlet.DispatcherServlet.render(ModelAndView, HttpServletRequest, HttpServletResponse)渲染頁面
檢查mv 是不是引用,好比是字符串,並進行相應view 的解析
1.org.springframework.web.servlet.DispatcherServlet.resolveViewName(String, Map<String, Object>, Locale, HttpServletRequest)調用viewResolver.resolveViewName(viewName,locale);
2. view.render(mv.getModelInternal(), request, response)
G.最後調用interceptor的org.springframework.web.servlet.HandlerInterceptor.afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)