本系列文章主要對SpringMVC源碼做詳細介紹,面向對Spring有所瞭解的Java程序員程序員
本文擬解惑幾個問題
1.http請求如何映射到Controller的
2.Controller是如何被執行的web
在有SpringMVC以前,咱們要對外提供http接口,那麼咱們須要編寫HttpServlet,並在web.xml中配置http請求到HttpServlet的映射關係
在有SpringMVC以後,開發人員開始寫Controller並用註解@
RequestMapping("\test")定義http請求到Controller.method()的映射關係spring
從HttpServlet到Controller實際上是由於SpringMVC對HttpServlet進行了一層封裝,對外提供一個接收全部請求的HttpServlet(DispatcherServlet),Spring把Controller加了@
RequestMapping的方法定義爲HandlerMethod,由DispatcherServlet統一分發http請求到對應的HandlerMethod去處理mvc
因此SpringMVC項目咱們都要在web.xml中配上全部請求到DispatcherServlet的映射app
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
DispatcherServlet的本質是一個HttpServlet
核心的方法有框架
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { doDispatch(request, response); } protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
父類FrameworkServlet重寫了HttpServlet的doGet()、doPost()等方法,最後都會調用DispatcherServlet的doService()方法,而後核心的執行流程在doDispatch()方法中ide
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; try { ModelAndView mv = null; Exception dispatchException = null; try { //由HandlerMapping找處理該請求的HandlerMethod和攔截該請求的HandlerInterceptor,包裝成HandlerExecutionChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 找到該HandlerMethod的HandlerAdapter(具體的方法調用流程) HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //執行HandlerInterceptor if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. 實際調用HandlerMethod mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //執行HandlerInterceptor mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } //返回渲染視圖 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; } protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (ha.supports(handler)) { return ha; } } }
這裏涉及到兩個核心的類
1.HandlerMappingthis
Interface to be implemented by objects that define a mapping between
requests and handler objects.
2.HandlerAdapterurl
- MVC framework SPI, allowing parameterization of the core MVC workflow.
Interface that must be implemented for each handler type to handle a request.
如官方文檔裏提到的,HandlerMapping定義了request和handler的映射關係,通俗點來講就是url對應的是哪一個HandlerMethod
public interface HandlerMapping { HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
核心方法返回HandlerExecutionChain,包裝了當前請求須要執行的Handler和interceptors
public class HandlerExecutionChain { private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class); private final Object handler; private HandlerInterceptor[] interceptors;
在DispatcherServlet.getHandler中
遍歷容器中全部的HandlerMapping,爲當前請求找到最匹配的HandlerMethod,包裝成HandlerExecutionChain,SpringMVC默認有不少HandlerMapping實現策略,每一個HandlerMapping中註冊着不一樣的HandlerMethod映射關係
咱們項目中默認對於Controller+@RequestMapping形式的HandlerMapping實現類爲RequestMappingHandlerMapping
他的父類的內部中維護了請求和HandlerMethod的映射關係
class MappingRegistry { private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
下一期咱們將詳細闡述HandlerMapping
DispatcherServlet.getHandlerAdapter(Object handler)方法爲當前handler找到合適的HandlerAdapter
從文檔定義能夠看出,HandlerAdapter定義了一個handler執行的流程,大體包括如下流程
1.請求參數解析成調用handler的參數
2.經過反射調用handler
3.處理handler的返回值
Spring上下文中有3個默認的HandlerAdapter實現,@RequestMapping定義的handler默認由RequestMappingHandlerAdapter處理
handler被交由RequestMappingHandlerAdapter的handle(HttpServletRequest request, HttpServletResponse response, Object handler)方法執行
通過層層調用,在invokeHandlerMethod()中定義了執行流程
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { //包裝請求,提供額外方法 ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); //包裝執行器 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);//參數解析 invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);//返回值處理 invocableMethod.setDataBinderFactory(binderFactory);//參數解析時特殊類型處理 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); //執行調用 invocableMethod.invokeAndHandle(webRequest, mavContainer); return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
咱們先來看看invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //執行調用 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); try {//處理返回值 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } } public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //獲取參數 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); // Object returnValue = doInvoke(args); return returnValue; }
最後實際調用Controller的方法的就是doInvoke中,傳入參數,利用反射進行調用
protected Object doInvoke(Object... args) throws Exception { ReflectionUtils.makeAccessible(getBridgedMethod()); try { return getBridgedMethod().invoke(getBean(), args); }
以上就是一次htpp請求的調用過程,下一篇文章咱們將詳細闡述handleMethod的註冊過程
文章的末尾咱們來看看DispatcherServlet的繼承結構
用於HTTP請求handler/controllers的中央調度程序,例如用於Web UI controllers或基於HTTP的遠程服務導出程序。調度註冊的handlers以處理Web請求,提供方便的url映射和異常處理功能。
這個servlet很是靈活:它能夠在任何工做流程中使用,只要實現了合適的adapter類。它提供瞭如下功能,將其與其餘請求驅動的Web MVC框架區分開來:
1.基於JavaBeans配置機制。
2.它可使用任何HandlerMapping實現 - 預先構建或做爲應用程序的一部分提供 - 控制分發 requests 到 handler objects。默認是 BeanNameUrlHandlerMapping和 RequestMappingHandlerMapping。HandlerMapping對象能夠定義爲servlet應用程序上下文中的bean,實現HandlerMapping接口,覆蓋默認的HandlerMapping(若是存在)。
3.它可使用任何HandlerAdapter; 這容許使用任何處理程序接口。默認適配器HttpRequestHandlerAdapter, 默認 RequestMappingHandlerAdapter 也會被註冊。HandlerAdapter對象能夠在應用程序上下文中做爲bean添加,並覆蓋默認的HandlerAdapter。
4。調度程序的exception解決策略能夠經過一個HandlerExceptionResolver例如映射到錯誤頁面的特定例外來指定 。默認的是 ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver和 DefaultHandlerExceptionResolver。這些HandlerExceptionResolvers能夠經過應用程序上下文重寫。
5.其視圖解析策略能夠經過ViewResolver 實現來指定,將符號視圖名稱解析爲視圖對象。默認是 InternalResourceViewResolver。能夠在應用程序上下文中將ViewResolver對象添加爲bean,覆蓋默認的ViewResolver。
注意:@RequestMapping只有在調度程序中存在相應HandlerMapping和HandlerAdapter時纔會處理。 這是默認狀況。可是,若是您正在定義自定義HandlerMappings 或者HandlerAdapters,那麼您須要確保相應的自定義 RequestMappingHandlerMapping和/或RequestMappingHandlerAdapter 定義 - 只要您打算使用@RequestMapping。
Web應用程序能夠定義任意數量的DispatcherServlet。 每一個servlet將在其本身的名稱空間中運行,使用映射,處理程序等加載其本身的應用程序上下文。只有ContextLoaderListener共享根據應用程序上下文。
核心方法
/** * Process the actual dispatching to the handler. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {