1、HandlerMappingweb
做用是根據當前請求的找到對應的 Handler,並將 Handler(執行程序)與一堆 HandlerInterceptor(攔截器)封裝到 HandlerExecutionChain 對象中。在 HandlerMapping 接口的內部只有一個方法,以下:spring
HandlerMapping 是由 DispatcherServlet 調用,DispatcherServlet 會從容器中取出全部 HandlerMapping 實例並遍歷,讓 HandlerMapping 實例根據本身實現類的方式去嘗試查找 Handler,而 HandlerMapping 具體有哪些實現類下面就會詳細分析。session
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 這些 HandlerMapping 在容器初始化時建立,在 initHandlerMappings 時放入集合中 for (HandlerMapping hm : this.handlerMappings) { HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
另外上面說到的 Handler 有多是一個 HandlerMethod(封裝了 Controller 中的方法)對象,也有多是一個 Controller 對象、 HttpRequestHandler 對象或 Servlet 對象,而這個 Handler 具體是什麼對象,也是與所使用的 HandlerMapping 實現類有關。以下圖所示,能夠看到 HandlerMapping 實現類有兩個分支,分別繼承自 AbstractHandlerMethodMapping(獲得 HandlerMethod)和 AbstractUrlHandlerMapping(獲得 HttpRequestHandler、Controller 或 Servlet),它們又統一繼承於 AbstractHandlerMapping。mvc
先來看一下 AbstractHandlerMapping,它實現了 HandlerMapping 接口中的 getHandler() 方法,源碼以下所示app
@Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 根據請求獲取執行程序,具體的獲取方式由子類決定,getHandlerInternal() 是抽象方法 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); } // 將 Handler 與一堆攔截器包裝到 HandlerExecutionChain 對象中 return getHandlerExecutionChain(handler, request); }
能夠看到在這個方法中又調用了 getHandlerInternal() 方法獲取到了 Handler 對象,而 Handler 對象具體內容是由它的子類去定義的。下面就來一看下 AbstractHandlerMapping 的兩個分支子類jsp
1 AbstractUrlHandlerMappingasync
AbstractUrlHandlerMapping 這個分支獲取的 Handler 的類型實際就是一個 Controller 類,因此一個 Controller 只能對應一個請求(或者像 Struts2 那樣定位到方法,使同一個業務的方法放在同一個類裏),源碼以下所示ide
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { // 根據當前請求獲取「查找路徑」 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 根據路徑獲取 Handler(即Controller),先嚐試直接匹配,再嘗試模式匹配 Object handler = lookupHandler(lookupPath, request); if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; }
1.1 AbstractUrlHandlerMapping 實現類及使用ui
1) ControllerClassNameHandlerMapping:根據類名訪問 Controller。this
<!-- 註冊 HandlerMapping --> <bean class="org.springframework.web.servlet.handler.ControllerClassNameHandlerMapping" /> <!-- 註冊 Handler --> <bean class="com.controller.TestController" />
2) ControllerBeanNameHandlerMapping:根據 Bean 名訪問 Controller,與 BeanNameUrlHandlerMapping 相似,可是bean名稱不用遵循URL公約。
<!-- 註冊 HandlerMapping --> <bean class="org.springframework.web.servlet.handler.ControllerBeanNameHandlerMapping" /> <!-- 註冊 Handler --> <bean id="test" class="com.controller.TestController" />
3) BeanNameUrlHandlerMapping:利用 BeanName 來做爲 URL 使用。
<!-- 註冊 HandlerMapping --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /> <!-- 註冊 Handler --> <bean id="/test.do" class="com.controller.TestController" />
4) SimpleUrlHandlerMapping:能夠將 URL 與處理器的定義分離,還能夠對 URL 進行統一的映射管理。
<!-- 註冊 HandlerMapping --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/test.do">testController</prop> <prop key="/hello.do">testController</prop> </props> </property> </bean> <!-- 註冊 Handler --> <bean id="testController" class="com.controller.TestController" />
1.2 Controller 類
使用 AbstractUrlHandlerMapping 的實現類時,須要讓控制層的類實現 Controller 接口(通常繼承 AbstractController 便可),另外還有一些已經實現了的 Controller 類,以下圖所示。可是不管是本身實現 Controller 接口仍是使用系統已經實現的類,都只能處理一個請求(除了 MultiActionController 能夠經過參數的方式讓一個類能夠處理多個請求)。
另外下面全部的 Controller 均採用 SimpleUrlHandlerMapping 方式的。
1) UrlFilenameViewController:用於跳轉界面,控制器根據請求的URL直接解析出視圖名,省去了本身實現 Ccntroller 跳轉頁面。
<bean id="indexController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
2) ParameterizableViewController:一樣用於界面跳轉,控制器根據配置的參數來跳轉界面,使用方式以下
<bean id="indexController" class="org.springframework.web.servlet.mvc.ParameterizableViewController"> <property name="viewName" value="/index.jsp" /> </bean>
3) ServletForwardingController:將請求轉發到 Servlet,使用方式以下
<bean id="indexController" class="org.springframework.web.servlet.mvc.ServletForwardingController"> <property name="servletName" value="indexServlet" /> </bean>
另外還要在 web.xml 中配置要轉發到的 Servlet
<servlet> <servlet-name>indexServlet</servlet-name> <servlet-class>com.servlet.ServletForwarding</servlet-class> </servlet>
4) ServletWrappingController:將某個 Servlet 包裝爲 Controller,全部到 ServletWrappingController 的請求其實是由它內部所包裝的這個 Servlet 實例來處理的,這樣能夠將這個 Servlet 隱藏起來
5) MultiActionController:一個 Controller 能夠寫多個方法,分別對應不一樣的請求,使同一業務的方法能夠放在一塊兒了。在使用時讓本身的 Controller 類繼承 MultiActionController 類,使用方式以下
public class IndexController extends MultiActionController { public ModelAndView add(HttpServletRequest request,HttpServletResponse response) { ModelAndView mv = new ModelAndView(); mv.addObject("message","add"); mv.setViewName("add"); return mv; } public ModelAndView delete(HttpServletRequest request,HttpServletResponse response) { ModelAndView mv = new ModelAndView(); mv.addObject("message","delete"); mv.setViewName("delete"); return mv; } }
配置本身的 Controller 時要配置一個方法名解析器(默認是 InternalPathMethodNameResolver )
<bean id="indexController" class="com.controller.IndexController"> <property name="methodNameResolver"> <!-- InternalPathMethodNameResolver 根據請求路徑解析執行方法名 ParameterMethodNameResolver 根據參數解析執行方法名 PropertiesMethodNameResolver 根據 key/value 列表解析執行方法名 --> <bean class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> <!-- 指定參數名爲action --> <property name="paramName" value="action" /> </bean> </property> </bean>
當咱們訪問 http://localhost:8080/***/indexAction.do?action=add 時,進入 add() 方法;
當咱們訪問 http://localhost:8080/***/indexAction.do?action=delete 時,進入 delete() 方法。
2 AbstractHandlerMethodMapping
AbstractHandlerMethodMapping 這個分支獲取的 Handler 的類型是 HandlerMethod,即這個 Handler 是一個方法,它保存了方法的信息(如Method),這樣一個 Controller 就能夠處理多個請求了,源碼以下所示
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 根據當前請求獲取「查找路徑」 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 獲取當前請求最佳匹配的處理方法(即Controller類的方法中) HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); }
上述代碼中 lookupHandlerMethod() 方法主要工做是在 Map<T, HandlerMethod> handlerMethods 中找到 HandlerMethod,這裏的 T 是 HandlerMappingInfo,它封裝了 @RequestMapping 註解中的信息。那 HandlerMethod 是怎麼建立的(即怎麼把 Controller 的方法變成了它),繼續看一下源碼找到 initHandlerMethods() 方法,這個方法是在這個類建立後調用的,以下所示是它的源碼
protected void initHandlerMethods() { // 從容器中獲取全部 Bean 的名稱,detectHandlerMethodsInAncestorContexts 默認false,不從父容器中查找 //即默認只查找 SpringMVC 的 IOC 容器,不查找它的父容器 Spring 的 IOC 容器 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName : beanNames) { // 這裏的 isHandler()方法由子類實現,判斷是否擁有 @Controller 註解或 @RequestMapping 註解 if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) && isHandler(getApplicationContext().getType(beanName))){ // 利用反射獲得 Bean 中的 Method 幷包裝成 HandlerMethod,而後放入 Map 中 detectHandlerMethods(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
看完上述代碼後,能夠知道是在 detectHandlerMethods() 方法中將 Bean 的方法轉換爲 HandlerMethod 對象,具體實現以下
protected void detectHandlerMethods(final Object handler) { // 獲取這個 Bean 的 Class 對象 Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass()); // 避免重複調用 getMappingForMethod(),getMappingForMethod() 將從新構建 RequestMappingInfo 實例 final Map<Method, T> mappings = new IdentityHashMap<Method, T>(); // 獲取被代理前的原始類型 final Class<?> userType = ClassUtils.getUserClass(handlerType); // 獲取 Method Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() { @Override public boolean matches(Method method) { // 根據 Method 和它的 @RequestMapping 註解,建立 RequestMappingInfo 對象。 // 這裏的 T 就是 RequestMappingInfo,它封裝了 @RequestMapping 信息 T mapping = getMappingForMethod(method, userType); if (mapping != null) { mappings.put(method, mapping); return true; } else { return false; } } }); for (Method method : methods) { // 註冊 Method 和它的映射,RequestMappingInfo 儲存着映射信息 registerHandlerMethod(handler, method, mappings.get(method)); } }
最後在 registerHandlerMethod() 方法中,將 RequestMappingInfo 做爲 key,把 Method 包裝成 HandlerMethod 做爲 value 添加到了 Map<T, HandlerMethod> handlerMethods 中。
protected void registerHandlerMethod(Object handler, Method method, T mapping) { HandlerMethod newHandlerMethod = createHandlerMethod(handler, method); HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping); if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) { throw new IllegalStateException(""); } this.handlerMethods.put(mapping, newHandlerMethod); Set<String> patterns = getMappingPathPatterns(mapping); for (String pattern : patterns) { if (!getPathMatcher().isPattern(pattern)) { this.urlMap.add(pattern, mapping); } } }
1.1 AbstractHandlerMapping 實現類及使用
AbstractHandlerMethodMapping<T> 只有一個實現類 RequestMappingHandlerMapping
2、HandlerAdapter
根據 Handler 來找到支持它的 HandlerAdapter,經過 HandlerAdapter 執行這個 Handler 獲得 ModelAndView 對象。HandlerAdapter 接口中的方法以下:
1 RequestMappingHandlerAdapter
從上面的文章中能夠知道,利用 RequestMappingHandlerMapping 獲取的 Handler 是 HadnlerMethod 類型,它表明 Controller 裏要執行的方法,而 RequestMappingHandlerAdapter 能夠執行 HadnlerMethod 對象。
RequestMappingHandlerAdapter 的 handle() 方法是在它的父類 AbstractHandlerMethodAdapter 類中實現的,源碼以下所示
@Override public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler); }
handleInternal() 方法是由 RequestMappingHandlerAdapter 本身來實現的,源碼以下所示
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // 是否經過 @SessionAttributes 註釋聲明瞭 session 屬性。 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true); } else { checkAndPrepare(request, response, true); } // 是否須要在 synchronize 塊中執行 if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { // 執行 HandlerMethod return invokeHandleMethod(request, response, handlerMethod); } } } // 執行 HandlerMethod,獲得 ModelAndView return invokeHandleMethod(request, response, handlerMethod); }
繼續再來看一下如何獲得 ModelAndView,invokeHandlerMethod() 方法以下
private ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // ServletWebRequest webRequest = new ServletWebRequest(request, response); // 數據綁定 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 綁定參數,執行方法 ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory); // 建立模型和視圖容器 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); // 設置FlasgMap中的值 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); // 初始化模型 modelFactory.initModel(webRequest, mavContainer, requestMappingMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result); } requestMappingMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); }
2 HttpRequestHandlerAdapter
HttpRequestHandlerAdapter 能夠執行 HttpRequestHandler 類型的 Handler,源碼以下
@Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; }
3 SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter 能夠執行 Controller 類型的 Handler,源碼以下
@Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return ((Controller) handler).handleRequest(request, response); }
4 SimpleServletHandlerAdapter
SimpleServletHandlerAdapter 能夠執行 Servlet 類型的 Handler,源碼以下
@Override public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Servlet) handler).service(request, response); return null; }
3、HandlerExceptionResolver
負責處理異常的類,負責根據異常來設置 ModelAndView,而後交由 render 渲染界面。HandlerExecptionResolver 接口中只有一個方法,以下: