[size=medium]上一篇文章讓我們了解HandlerMapping和HandlerAdapter以及預設采取的政策,這篇文章就要講述mvc:annotation-driven對預設政策的改變。它背後到底注冊了哪些HandlerMapping和HandlerAdapter。
首先可以在DispatcherServlet的initStrategies方法中的initHandlerMappings和initHandlerAdapters中打上斷點,來檢視注冊了哪些HandlerMapping和HandlerAdapter[/size]
[size=medium]目前我的spring版本是4.0.5。我檢視的結果:
HandlerMapping:注冊了 RequestMappingHandlerMapping和BeanNameUrlHandlerMapping
HandlerAdapter:注冊了 RequestMappingHandlerAdapter、HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter
這幾個HandlerMapping和HandlerAdapter上文都提到過。
下面就要檢視下具體的注冊過程:
在xml檔案中配置mvc:annotation-driven,肯定有一個專門的類來解析處理這個東西。
會有這樣的一個接口BeanDefinitionParser,它隻有一個方法:[/size]
[size=medium]它是用來專門處理<beans></beans>裡面的配置元素。然後我們會找到這樣的一個實作類AnnotationDrivenBeanDefinitionParser,它的文檔介紹如下:[/size]
[size=medium]上面的文檔對mvc:annotation-driven注冊的東西都有詳細的說明。
具體看解析過程的代碼的内容:[/size]
[size=medium]MvcNamespaceUtils.registerDefaultComponents的内容如下:[/size]
[size=medium]至此所注冊的HandlerMapping和HandlerAdapter我們都找到了。
然後我們就可以體驗下RequestMappingHandlerMapping和BeanNameUrlHandlerMapping,這兩個HandlerMapping。由于上一篇文章已經體驗過了BeanNameUrlHandlerMapping,接下來就要體驗下RequestMappingHandlerMapping,然後你會發覺又有一系列的新名詞走進我們的視野,需要我們去弄清楚。
先體驗下:
首先還是web.xml的配置:[/size]
[size=medium]最簡單的配置,然後是[servlet-name]-servlet.xml,本工程即mvc-servlet.xml:[/size]
[size=medium]開啟了<mvc:annotation-driven/>,同時注冊了兩個bean。有RequestMappingHandlerMapping和RequestMappingHandlerAdapter作為後盾支援,然後我們就可以在bean中使用@Controller和@RequestMapping兩個标簽了。@Controller本身其實與@RequestMapping無關的,它隻是@Component中的一個重要的标簽而已,但是我們會在源碼裡看到它對RequestMappingHandlerMapping也是挺重要的,但不是必須的。這裡簡單說明下:RequestMappingHandlerMapping它會判斷一個bean是否含有@Controller标簽或者@RequestMapping,如果有其一則會将該bean納入作為它的處理對象,之後會進一步處理該類上含有@RequestMapping注解的方法。這樣做主要是由于@RequestMapping可以配置在類上(作為基礎位址),也可以配置在方法上,我們有時候會在類上配置@RequestMapping,有時候又不會,是以隻要類含有@Controller或者含有@RequestMapping,RequestMappingHandlerMapping都會将他們納入自己的handler管轄範圍。是以僅僅在方法中含有@RequestMapping注解是不被處理的,必須在類上加入@RequestMapping或者@Controller,而@Controller又不是必須的,你可以試驗下,稍後會做源代碼說明。下面繼續,列出使用了@Controller和@RequestMapping注解的StringAction類[/size]
[size=medium]然後就可以運作一下,體驗一下,先不要管亂碼問題,這個問題引出了下一篇文章spring架構中的亂碼問題。
運作結果如下:
[/size]
[img]http://dl2.iteye.com/upload/attachment/0100/2900/49a4b62a-efdf-3256-8b29-7e8583eb396c.png[/img]
[size=medium]證明整個流程跑通了。
首先@Controller使得StringAction這個handler納入RequestMappingHandlerMapping管理,RequestMappingHandlerMapping會将這個handler和handler中的每一個含有@RequestMapping的方法都會建構成一個HandlerMethod對象,該類的構造函數為HandlerMethod(Object bean, Method method),經過這樣的包裝之後将構造的HandlerMethod對象作為新的handler,然後進行選擇擴充卡,進行方法調用,當RequestMappingHandlerAdapter判斷是否support一個類時,就是依據目前的handlelr是否是HandlerMethod類型。若是則由RequestMappingHandlerAdapter來排程執行該handler(handler為HandlerMethod類型)的中的method方法。以上就是整個大體的流程。下面就要用代碼來事實說話:
第一步要弄清RequestMappingHandlerMapping在初始化時是如何尋找它所管轄的bean。說說我找代碼的具體流程:
RequestMappingHandlerMapping的父類AbstractHandlerMethodMapping在初始化時,會調用到這樣的一個方法initHandlerMethods,在該方法中,周遊所有的bean然後判斷他們是不是含有@Controller或者@RequestMapping注解:[/size]
[size=medium]其中的isHandler的判斷方法代碼如下:[/size]
[size=medium]如果handler含有了上述注解的其中之一,就會進一步處理該handler的方法中含有@RequestMapping的方法:[/size]
[size=medium]周遊這個handler類的所有方法,過濾條件就是這個内部類MethodFilter,其中的getMappingForMethod方法内容為:[/size]
[size=medium]如找到了含有RequestMapping注釋的方法,則由這個注釋的内容建構一個RequestMappingInfo對象:[/size]
[size=medium]就是拿RequestMapping注釋的内容進一步封裝進RequestMappingInfo對象中。對handler的所有方法過濾完成之後,就要周遊這些方法,以一定的方式存儲起來。[/size]
[size=medium]這裡的this.handlerMethods就包含了所有管轄的bean,key為RequestMappingInfo對象,value為handler和它中含有@RequestMapping注釋的方法method建構的HandlerMethod。
如下所示:[/size]
[size=medium]至此,RequestMappingHandlerMapping的初始化注冊工作就完成了。然後就是等待請求,通路
http://localhost:8080/string?name=aa,RequestMappingHandlerMapping會比對到由StringAction對象和它的包含注釋的方法testMessageConverter建構的HandlerMethod對象,該對象将作為handler,然後再周遊HandlerAdapter判斷它們是否支援這個handler,RequestMappingHandlerAdapter的判斷依據為是否是HandlerMethod 類型(在AbstractHandlerMethodAdapter類中):[/size]
[size=medium]然後将得到比對,有了這個HandlerMethod對象,便可以通過RequestMappingHandlerAdapter來排程執行HandlerMethod其中的方法。[/size]