SpringMVC原始碼總結(一)HandlerMapping和HandlerAdapter入門

zljjava發表於2015-12-27

剛接觸SpringMVC,對它的xml檔案配置一直比較模模糊糊,最近花了一點時間稍微看了下原始碼,再加上除錯,開始逐漸理解它,網上的類似的內容有很多,寫本文主要是自己加深一下理解。本文適合用過SpringMVC的開發者,言歸正傳,首先搭建一個最簡單的工程體驗一下。 

該工程是基於maven的,pom配置不再說明,所使用的spring版本4.0.5。 
首先是web.xml檔案配置,最簡單的配置 

Java程式碼  收藏程式碼
  1. <!DOCTYPE web-app PUBLIC  
  2.  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"  
  3.  "http://java.sun.com/dtd/web-app_2_3.dtd" >  
  4.   
  5. <web-app>  
  6.   <display-name>Archetype Created Web Application</display-name>  
  7.   <servlet>  
  8.         <servlet-name>mvc</servlet-name>  
  9.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  10.         <load-on-startup>1</load-on-startup>  
  11.     </servlet>  
  12.   
  13.     <servlet-mapping>  
  14.         <servlet-name>mvc</servlet-name>  
  15.         <url-pattern>/*</url-pattern>  
  16.     </servlet-mapping>  
  17. </web-app>  

然後是mvc-servlet.xml檔案的配置,上面配置DispatcherServlet會預設載入[servlet-name]-servlet.xml檔案。對於我的配置,會去載入mvc-servlet.xml檔案。 
mvc-servlet.xml檔案的內容:
 
Java程式碼  收藏程式碼
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context"  
  3.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  4.     http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
  5.     http://www.springframework.org/schema/mvc  
  6.     http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd  
  7.     http://www.springframework.org/schema/util  
  8.     http://www.springframework.org/schema/util/spring-util-2.0.xsd  
  9.     http://www.springframework.org/schema/context   
  10.     http://www.springframework.org/schema/context/spring-context-3.2.xsd">  
  11.   
  12.     <bean name="/index" class="com.lg.mvc.HomeAction"></bean>  
  13.     <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">  
  14.         <property name="templateLoaderPath" value="/WEB-INF/views" />  
  15.         <property name="defaultEncoding" value="utf-8" />  
  16.         <property name="freemarkerSettings">  
  17.             <props>  
  18.                 <prop key="locale">zh_CN</prop>  
  19.             </props>  
  20.         </property>  
  21.     </bean>  
  22.     <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">  
  23.         <property name="suffix" value=".html" />  
  24.         <property name="contentType" value="text/html;charset=utf-8" />  
  25.         <property name="requestContextAttribute" value="request" />  
  26.         <property name="exposeRequestAttributes" value="true" />  
  27.         <property name="exposeSessionAttributes" value="true" />  
  28.     </bean>  
  29. </beans>  

在該配置中定義了一個HomeAction的Bean。內容為: 
Java程式碼  收藏程式碼
  1. package com.lg.mvc;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5.   
  6. import org.springframework.web.servlet.ModelAndView;  
  7. import org.springframework.web.servlet.mvc.Controller;  
  8.   
  9. public class HomeAction implements Controller{  
  10.   
  11.     @Override  
  12.     public ModelAndView handleRequest(HttpServletRequest request,  
  13.             HttpServletResponse response) throws Exception {  
  14.         return new ModelAndView("hello");  
  15.     }  
  16. }  

這是最原始的mvc做法,要繼承Controller介面,先從原始的說起,最後再過渡到@Controller和@RequestMapping註解式的配置。它在mvc-serlet.xml檔案中的配置有一個關鍵的屬性name="/index"。 
WEB-INF/view目錄下有一個簡單的hello.html,內容為:
 
Java程式碼  收藏程式碼
  1. <html>  
  2.     <head>  
  3.       
  4.     </head>  
  5.     <body>  
  6.         hello lg !  
  7.     </body>  
  8. </html>  

至此該工程就寫完了,部署到tomcat中,專案路徑為/,執行一下。 
訪問 http://localhost:8080/index 
 
至此整個工程就算搭建成功了。 

下面就要說說原理了。 
用過python Django框架的都知道Django對於訪問方式的配置就是,一個url路徑和一個函式配對,你訪問這個url,就會直接呼叫這個函式,簡單明瞭。對於java的物件導向來說,就要分兩步走。第一步首先要找到是哪個物件,即handler,本工程的handler則是HomeAction物件。第二步要找到訪問的函式,即HomeAction的handleRequest方法。所以就出現了兩個原始碼介面 HandlerMapping和HandlerAdapter,前者負責第一步,後者負責第二步。借用網上的SpringMVC架構圖。 

HandlerMapping介面的實現(只舉了我認識的幾個) : 

    BeanNameUrlHandlerMapping :通過對比url和bean的name找到對應的物件 
    SimpleUrlHandlerMapping :也是直接配置url和對應bean,比BeanNameUrlHandlerMapping功能更多 
    DefaultAnnotationHandlerMapping : 主要是針對註解配置@RequestMapping的,已過時 
    RequestMappingHandlerMapping :取代了上面一個 

HandlerAdapter 介面實現: 

    HttpRequestHandlerAdapter : 要求handler實現HttpRequestHandler介面,該介面的方法為                                                             void handleRequest(HttpServletRequest request, HttpServletResponse response)也就是  handler必須有一個handleRequest方法 

    SimpleControllerHandlerAdapter:要求handler實現Controller介面,該介面的方法為ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也就是本工程採用的 

    AnnotationMethodHandlerAdapter :和上面的DefaultAnnotationHandlerMapping配對使用的,也已過時 

    RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配對使用,針對@RequestMapping 

先簡單的說下這個工程的流程,訪問http://localhost:8080/index首先由DispatcherServlet進行轉發,通過BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),找到了HomeAction,然後再拿HomeAction和每個adapter進行適配,由於HomeAction實現了Controller介面,所以最終會有SimpleControllerHandlerAdapter來完成對HomeAction的handleRequest方法的排程。然後就順利的執行了我們想要的方法,後面的內容不在本節中說明。 

瞭解了大概流程,然後就需要看原始碼了。 
首先就是SpringMVC的入口類,DispatcherServlet,它實現了Servlet介面,不再詳細說DispatcherServlet的細節,不然又是一大堆的內容。每次請求都會呼叫它的doService->doDispatch,我們關注的重點就在doDispatch方法中。 
Java程式碼  收藏程式碼
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  2.         HttpServletRequest processedRequest = request;  
  3.         HandlerExecutionChain mappedHandler = null;  
  4.         boolean multipartRequestParsed = false;  
  5.   
  6.         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  
  7.   
  8.         try {  
  9.             ModelAndView mv = null;  
  10.             Exception dispatchException = null;  
  11.   
  12.             try {  
  13.                 processedRequest = checkMultipart(request);  
  14.                 multipartRequestParsed = (processedRequest != request);  
  15.                       //這個是重點,第一步由HandlerMapping找到對應的handler  
  16.                 // Determine handler for the current request.  
  17.                 mappedHandler = getHandler(processedRequest);  
  18.                 if (mappedHandler == null || mappedHandler.getHandler() == null) {  
  19.                     noHandlerFound(processedRequest, response);  
  20.                     return;  
  21.                 }  
  22.   
  23.                 // Determine handler adapter for the current request.  
  24.                        //這是第二步,找到合適的HandlerAdapter,然後由它來排程執行handler的方法  
  25.                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  26.   
  27.                 // Process last-modified header, if supported by the handler.  
  28.                 String method = request.getMethod();  
  29.                 boolean isGet = "GET".equals(method);  
  30.                 if (isGet || "HEAD".equals(method)) {  
  31.                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());  
  32.                     if (logger.isDebugEnabled()) {  
  33.                         logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);  
  34.                     }  
  35.                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
  36.                         return;  
  37.                     }  
  38.                 }  
  39.   
  40.                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
  41.                     return;  
  42.                 }  
  43.   
  44.                 try {  
  45.                     // Actually invoke the handler.  
  46.                     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
  47.                 }  
  48.                 finally {  
  49.                     if (asyncManager.isConcurrentHandlingStarted()) {  
  50.                         return;  
  51.                     }  
  52.                 }  
  53.   
  54.                 applyDefaultViewName(request, mv);  
  55.                 mappedHandler.applyPostHandle(processedRequest, response, mv);  
  56.             }  
  57.             catch (Exception ex) {  
  58.                 dispatchException = ex;  
  59.             }  
  60.             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
  61.         }  
  62.         catch (Exception ex) {  
  63.             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  
  64.         }  
  65.         catch (Error err) {  
  66.             triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);  
  67.         }  
  68.         finally {  
  69.             if (asyncManager.isConcurrentHandlingStarted()) {  
  70.                 // Instead of postHandle and afterCompletion  
  71.                 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);  
  72.                 return;  
  73.             }  
  74.             // Clean up any resources used by a multipart request.  
  75.             if (multipartRequestParsed) {  
  76.                 cleanupMultipart(processedRequest);  
  77.             }  
  78.         }  
  79.     }  

第一步詳細檢視: 
Java程式碼  收藏程式碼
  1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  2.         for (HandlerMapping hm : this.handlerMappings) {  
  3.             if (logger.isTraceEnabled()) {  
  4.                 logger.trace(  
  5.                         "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");  
  6.             }  
  7.             HandlerExecutionChain handler = hm.getHandler(request);  
  8.             if (handler != null) {  
  9.                 return handler;  
  10.             }  
  11.         }  
  12.         return null;  
  13.     }  

可以看到就是通過遍歷所有已註冊的HandlerMapping來找到對應的handler,然後構建出一個HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些攔截器,如下 
Java程式碼  收藏程式碼
  1. public class HandlerExecutionChain {  
  2.   
  3.     private final Object handler;  
  4.   
  5.     private HandlerInterceptor[] interceptors;  
  6.   
  7.     private List<HandlerInterceptor> interceptorList;  
  8.           
  9.         //其他程式碼省略  
  10. }  

其中HandlerMapping的getHandler實現: 
Java程式碼  收藏程式碼
  1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  2.         Object handler = getHandlerInternal(request);  
  3.         if (handler == null) {  
  4.             handler = getDefaultHandler();  
  5.         }  
  6.         if (handler == null) {  
  7.             return null;  
  8.         }  
  9.         // Bean name or resolved handler?  
  10.         if (handler instanceof String) {  
  11.             String handlerName = (String) handler;  
  12.             handler = getApplicationContext().getBean(handlerName);  
  13.         }  
  14.         return getHandlerExecutionChain(handler, request);  
  15.     }  

這裡的getHandlerInternal(request)是個抽象方法,由具體的HandlerMapping來實現,獲取到的handler如果為空,則獲取預設配置的handler,如果handler為String型別,則表示這個則會去Spring容器裡面去找這樣名字的bean。 
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具體實現(通過一系列的介面設計,之後再好好看看這個設計,到BeanNameUrlHandlerMapping這隻用實現該方法中的一部分),如下
 
Java程式碼  收藏程式碼
  1. public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {  
  2.   
  3.     /** 
  4.      * Checks name and aliases of the given bean for URLs, starting with "/". 
  5.      */  
  6.     @Override  
  7.     protected String[] determineUrlsForHandler(String beanName) {  
  8.         List<String> urls = new ArrayList<String>();  
  9.         if (beanName.startsWith("/")) {  
  10.             urls.add(beanName);  
  11.         }  
  12.         String[] aliases = getApplicationContext().getAliases(beanName);  
  13.         for (String alias : aliases) {  
  14.             if (alias.startsWith("/")) {  
  15.                 urls.add(alias);  
  16.             }  
  17.         }  
  18.         return StringUtils.toStringArray(urls);  
  19.     }  
  20.   
  21. }  

這裡面註釋說,bean的name必須以/開頭,它才處理,將資訊儲存在Map<String, Object> handlerMap中,對於本工程來說就是{'/index':HomeAction物件}。 
至此這裡完成了第一步,下面開始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具體實現:
 
Java程式碼  收藏程式碼
  1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {  
  2.         for (HandlerAdapter ha : this.handlerAdapters) {  
  3.             if (logger.isTraceEnabled()) {  
  4.                 logger.trace("Testing handler adapter [" + ha + "]");  
  5.             }  
  6.             if (ha.supports(handler)) {  
  7.                 return ha;  
  8.             }  
  9.         }  
  10.         throw new ServletException("No adapter for handler [" + handler +  
  11.                 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");  
  12.     }  

遍歷所有的HandlerAdapter,判斷他們是否支援這個handler。 
我們來看下HttpRequestHandlerAdapter的supports(handler)方法:
 
Java程式碼  收藏程式碼
  1. public class HttpRequestHandlerAdapter implements HandlerAdapter {  
  2.   
  3.     @Override  
  4.     public boolean supports(Object handler) {  
  5.           //就是判斷handler是否實現了HttpRequestHandler介面  
  6.         return (handler instanceof HttpRequestHandler);  
  7.     }  
  8.   
  9.     @Override  
  10.     public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  11.             throws Exception {  
  12.            //若handler實現了HttpRequestHandler介面,則呼叫該介面的方法,執行我們在該方法中寫的業務邏輯  
  13.         ((HttpRequestHandler) handler).handleRequest(request, response);  
  14.         return null;  
  15.     }  
  16.   
  17.     @Override  
  18.     public long getLastModified(HttpServletRequest request, Object handler) {  
  19.         if (handler instanceof LastModified) {  
  20.             return ((LastModified) handler).getLastModified(request);  
  21.         }  
  22.         return -1L;  
  23.     }  
  24.   
  25. }  

同理SimpleControllerHandlerAdapter也是這樣類似的邏輯 
Java程式碼  收藏程式碼
  1. public class SimpleControllerHandlerAdapter implements HandlerAdapter {  
  2.   
  3.     @Override  
  4.     public boolean supports(Object handler) {  
  5.         return (handler instanceof Controller);  
  6.     }  
  7.   
  8.     @Override  
  9.     public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  10.             throws Exception {  
  11.   
  12.         return ((Controller) handler).handleRequest(request, response);  
  13.     }  
  14.   
  15.     @Override  
  16.     public long getLastModified(HttpServletRequest request, Object handler) {  
  17.         if (handler instanceof LastModified) {  
  18.             return ((LastModified) handler).getLastModified(request);  
  19.         }  
  20.         return -1L;  
  21.     }  
  22.   
  23. }  

剩餘兩個AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比較複雜,我也沒看。 
按照本工程的配置,則SimpleControllerHandlerAdapter是支援HomeAction的,然後就會執行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本質上就會呼叫HomeAction實現Controller介面的方法。至此就分析完了。 
瞭解過程了之後,然後就是最重要的也是經常配置出問題的地方。DispatcherServlet的handlerMappings和handlerAdapters的來源問題。 

DispatcherServlet初始化的時候,會呼叫一個方法如下: 

Java程式碼  收藏程式碼
  1. protected void initStrategies(ApplicationContext context) {  
  2.         initMultipartResolver(context);  
  3.         initLocaleResolver(context);  
  4.         initThemeResolver(context);  
  5. //初始化一些HandlerMapping  
  6.         initHandlerMappings(context);  
  7. //初始化一些HandlerAdapter  
  8.         initHandlerAdapters(context);  
  9.         initHandlerExceptionResolvers(context);  
  10.         initRequestToViewNameTranslator(context);  
  11.         initViewResolvers(context);  
  12.         initFlashMapManager(context);  
  13.     }  

這裡可以看到,它會初始化一些HandlerMapping和HandlerAdapter,這兩個方法非常重要,理解了這兩個方法你就會知道,配置不對問題出在哪裡,下面具體看下這兩個方法: 
Java程式碼  收藏程式碼
  1. private void initHandlerMappings(ApplicationContext context) {  
  2.         this.handlerMappings = null;  
  3.   
  4.         if (this.detectAllHandlerMappings) {  
  5.             // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.  
  6.             Map<String, HandlerMapping> matchingBeans =  
  7.                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.classtruefalse);  
  8.             if (!matchingBeans.isEmpty()) {  
  9.                 this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());  
  10.                 // We keep HandlerMappings in sorted order.  
  11.                 OrderComparator.sort(this.handlerMappings);  
  12.             }  
  13.         }  
  14.         else {  
  15.             try {  
  16.                 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);  
  17.                 this.handlerMappings = Collections.singletonList(hm);  
  18.             }  
  19.             catch (NoSuchBeanDefinitionException ex) {  
  20.                 // Ignore, we'll add a default HandlerMapping later.  
  21.             }  
  22.         }  
  23.   
  24.         // Ensure we have at least one HandlerMapping, by registering  
  25.         // a default HandlerMapping if no other mappings are found.  
  26.         if (this.handlerMappings == null) {  
  27.             this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);  
  28.             if (logger.isDebugEnabled()) {  
  29.                 logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");  
  30.             }  
  31.         }  
  32.     }  

detectAllHandlerMappings是DispatcherServlet的一個屬性,你是可以在web.xml中配置的,預設是true,如果為true,則會去從本工程mvc-servlet.xml檔案中去探測所有實現了HandlerMapping的bean,如果有,則加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings為false,則直接去容器中找id="handlerMapping"且實現了HandlerMapping的bean.如果以上都沒找到,則會去載入預設的HandlerMapping。 
Java程式碼  收藏程式碼
  1. /** Detect all HandlerMappings or just expect "handlerMapping" bean? */  
  2.     private boolean detectAllHandlerMappings = true;  

本工程由於沒有配置HandlerMapping,所以它會去載入預設的,下面看看預設的配置是什麼 
Java程式碼  收藏程式碼
  1. protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {  
  2.         String key = strategyInterface.getName();  
  3. //defaultStrategies儲存了預設的配置  
  4.         String value = defaultStrategies.getProperty(key);  
  5.         if (value != null) {  
  6.             String[] classNames = StringUtils.commaDelimitedListToStringArray(value);  
  7.             List<T> strategies = new ArrayList<T>(classNames.length);  
  8.             for (String className : classNames) {  
  9.                 try {  
  10.                     Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());  
  11.                     Object strategy = createDefaultStrategy(context, clazz);  
  12.                     strategies.add((T) strategy);  
  13.                 }  
  14.                 catch (ClassNotFoundException ex) {  
  15.                     throw new BeanInitializationException(  
  16.                             "Could not find DispatcherServlet's default strategy class [" + className +  
  17.                                     "] for interface [" + key + "]", ex);  
  18.                 }  
  19.                 catch (LinkageError err) {  
  20.                     throw new BeanInitializationException(  
  21.                             "Error loading DispatcherServlet's default strategy class [" + className +  
  22.                                     "] for interface [" + key + "]: problem with class file or dependent class", err);  
  23.                 }  
  24.             }  
  25.             return strategies;  
  26.         }  
  27.         else {  
  28.             return new LinkedList<T>();  
  29.         }  
  30.     }  

繼續看看defaultStrategies是如何初始化的: 
Java程式碼  收藏程式碼
  1. private static final Properties defaultStrategies;  
  2.   
  3.     static {  
  4.         // Load default strategy implementations from properties file.  
  5.         // This is currently strictly internal and not meant to be customized  
  6.         // by application developers.  
  7.         try {  
  8. //這裡的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties  
  9.             ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);  
  10.             defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);  
  11.         }  
  12.         catch (IOException ex) {  
  13.             throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());  
  14.         }  
  15.     }  

這裡使用靜態程式碼塊來載入配置檔案DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目錄下面的,如下圖所示: 


該預設的配置檔案的內容如下: 
Java程式碼  收藏程式碼
  1. # Default implementation classes for DispatcherServlet's strategy interfaces.  
  2. # Used as fallback when no matching beans are found in the DispatcherServlet context.  
  3. # Not meant to be customized by application developers.  
  4.   
  5. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver  
  6.   
  7. org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver  
  8.   
  9. #這裡就是預設的HandlerMapping的配置  
  10. org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\  
  11.     org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping  
  12. #這裡就是預設的HandlerAdapter的配置  
  13. org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\  
  14.     org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\  
  15.     org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter  
  16.   
  17. org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\  
  18.     org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\  
  19.     org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver  
  20.   
  21. org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator  
  22.   
  23. org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver  
  24.   
  25. org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager  

也就是說,當你什麼都沒有配置時,預設會載入以上的配置。正是由於有了上述預設配置的BeanNameUrlHandlerMapping(它要求name必須是以/開頭的),它才會儲存我們在mvc-servlet.xml中配置的<bean name="/index" class="com.lg.mvc.HomeAction"></bean>,同樣正是由於有了SimpleControllerHandlerAdapter(由於handler實現了Controller介面,所以它的support方法支援我們的handler),才會排程執行HomeAction的handleRequest方法。

相關文章