天天看点

SpringMVC源码总结(二)mvc:annotation-driven以及@Controller和@RequestMapping的那些事

[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]

继续阅读