SpringMVC源码阅读:定位Controller

1.前言SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧本文将通过源码分析,弄清楚SpringMVC如何找到我们定义的Controller2.源码分析org.springframework.web包有49017行代码,我们不可能去逐行阅读,那么,如何寻找源码的踪迹?顺着上篇博文的思路回到Di...

SpringMVC源码阅读:定位Controller

1.前言

SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧

本文将通过源码分析,弄清楚SpringMVC如何找到我们定义的Controller

2.源码分析

org.springframework.web包有49017行代码,我们不可能去逐行阅读,那么,如何寻找源码的踪迹?

顺着上篇博文的思路回到DispatcherServlet类的doDispatch方法,我们获取到了HandlerExecutionChain

点开getHandler方法,发现HandlerExecutionChain是通过HandlerMapping获取的。

DispatcherServlet分析参见SpringMVC源码阅读:核心分发器DispatcherServlet

943行,我们在这里获取HandlerAdapter,获取到各种argumentResolvers,用来解析参数,还能获取到各种returnValueHandlers

对HandlerMapping ctrl h查看类继承关系,找到RequestMappingHandlerMapping

我们找到了核心类RequestMappingHandlerMapping,注释说,该类用于创建@RequestMapping和@Controller注解的实例,在3.1版本加入,这正是我们所需要的

根据官方文档提示,我们在调用RequestMappingHandlerMapping的时候真正调用的是HandlerMethod,打开HandlerMethod(注意是org.springframework.web包下)

HandlerMethod是3.1版本引入的,为参数、返回值和注解提供便捷的封装

打开HandlerMethod的子类InvocableHandlerMethod,发现有WebDataBinderFactory,HandlerMethodArgumentResolverComposite,ParameterNameDiscoverer,这三个属性显然是用来处理请求参数的

再打开InvocableHandlerMethod的子类ServletInvocableHandlerMethod,发现有HandlerMethodReturnValueHandlerComposite,这个属性显然是用来处理响应、返回值的

打开HandlerAdpter的子类RequestMappingHandlerAdapter,我们看看它到底做了什么

实例化ServletInvocableHandlerMethod,注入HandlerMethodArgumentResolverComposite,HandlerMethodReturnValueHandlerComposite和ParameterNameDiscoverer,在4.2版本以前,ServletInvocableHandlerMethod在createRequestMappingMethod方法中实例(已废弃,该方法已删除)

现在我们看看HandlerMethod子类InvocableHandlerMethod到底做了什么,浏览器输入http://localhost:8080/springmvcdemo/employee/detail/1

 @RequestMapping(method = RequestMethod.GET, value = "/detail/{employeeId}",produces={"application/json; charset=UTF-8"}) @ResponseBody public ModelAndView detail(@PathVariable Integer employeeId, ModelAndView view) {  view.setViewName("employee/form");  view.addObject("employee", employeeService.getById(employeeId));  view.addObject("depts", deptService.listAll());  return view; }

147行获取参数详细信息,如参数名称,148行之后解析参数值Value

parameters在MethodParameter初始化完毕后的数据如下

我们有Integer和ModelAndView类型的两个参数,第一个参数指明了名称叫做"employeeId",第二个未指明名称,所以为null。现在,我们打开MethodParameter类,看看这些参数信息是从哪里来的

在MethodParameter类里,我们看到了参数的原始形态,仅有parameterIndex和nestingLevel。parameterIndex=0表示第一个参数,等于2表示第二个参数,以此类推

这里定义了拷贝构造函数,初始化参数的详细信息,获取每个属性的方法园友可以自行打断点查看,不再逐个赘述。

再回到InvocableHandlerMethod类,回到148行,我们看看参数值是如何获取的,先看args是什么

不难理解,第一个参数"employeeId"=1,第二个参数是null,继续往下走

148行声明Object数组,存储参数值,151行初始化ParameterNameDiscovery,仅为之后方法调用可以使用discover,并未真正解析参数

158~159行,调用HandlerMethodArgumentResolverComposite解析并得到参数值

回到RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping,我们看看initHandlerMethods方法做了什么,启动服务会进入该方法

197~199行初始化所有Bean并获取beanName

205行获取Bean类型

213行isHandler判断bean类型是不是RequestMapping或者Controller,在子类RequestMappingHandlerMapping实现

214行寻找HandlerMethod,218行调用所有被侦测到的HandlerMethod

点开detectHandlerMethods方法,看看它具体做了什么

226~228利用反射依次获取Controller,打断点我发现handlerType和userType内容是一致的,getUserClass方法注释说明是为了获取指定handlerType的类,我觉得这一步多此一举

230行依次获取Controller所有方法

235行getMappingForMethod方法由当前类(AbstractHandlerMethodMapping)的子类RequestMappingHandlerMapping实现,返回RequestMappingInfo

248行遍历methods,取出方法,249行泛型T是RequestMappingInfo

250行注册HandlerMethod

打破砂锅弄到底,继续进入getMappingForMethod方法,ctrl alt b快速跳转到子类实现

再进入createRequestMappingInfo方法

205行依次获取Controller的方法上@RequestMapping注解的属性

208行requestMapping不为空则调用createRequestMappingInfo重载方法(两个参数)用来构造RequestMappingInfo

208行点进去createRequestMappingInfo重载方法,一探究竟

继续深入,进入RequestMappingInfo,看这个类在做什么,按ctrl alt u,看类的接口实现关系

看来,RequestMappingInfo实现了RequestCondition,在RequestMappingInfo类中我们观察combine方法,它把路径、方法、参数、头等信息进行了combine操作,combine是指将两个或多个实例根据规则进行组合

打开RequestCondition子类PatternsRequestCondition,找到combine方法,在169行ctrl alt b跳转至实现类AntPathMatcher combine方法,此方法初始化才会进入

550行以"/*"结尾,截取再拼接,作者注释很清楚,不赘述

556行以"/**"结尾,直接拼接,请看注释

其他的AbstractRequestCondition子类不再介绍,园友可以自己断点调试看看

3.测试

写一个Controller

@Controller@RequestMapping("wildcard")public class TestWildcardController { @RequestMapping("/test/**") @ResponseBody public String test1(ModelAndView view) {
源文地址:https://www.guoxiongfei.cn/cntech/9083.html