一 Spring MVC 请求处理流程 引用 Spring in Action 上的一张图来说明了 SpringMVC 的核心组件和请求处理流程:
①:DispatcherServlet 是 SpringMVC 中的前端控制器(Front Controller),负责接收 Request 并将 Request 转发给对应的处理组件.
②:HanlerMapping 是 SpringMVC 中完成 url 到 Controller 映射的组件.DispatcherServlet 接收 Request,然后从 HandlerMapping 查找处理 Request 的 Controller.
③:Cntroller 处理 Request,并返回 ModelAndView 对象,Controller 是 SpringMVC 中负责处 理 Request 的组件(类似于 Struts2 中的 Action),ModelAndView 是封装结果视图的组件.
④ :视图解析器解析 ModelAndView 对象并返回对应的视图给客户端.
二 Spring MVC 的工作机制
在容器初始化时会建立所有 url 和 Controller 的对应关系,保存到 Map
三 Spring MVC 源码分析
我们根据工作机制中三部分来分析 SpringMVC 的源代码.。 其一,ApplicationContext 初始化时建立所有 url 和 Controller 类的对应关系(用 Map 保存); 其二,根据请求 url 找到对应的 Controller,并从 Controller 中找到处理请求的方法; 其三,request 参数绑定到方法的形参,执行方法处理请求,并返回结果视图. 第一步、建立 Map
determineUrlsForHandler(String beanName)方法的作用是获取每个 Controller 中的 url,不同 的子类有不同的实现,这是一个典型的模板设计模式.因为开发中我们用的最多的就是用注解来配置 Controller 中 的 url,BeanNameUrlHandlerMapping 是 AbstractDetectingUrlHandlerMapping 的子类,处理注解形式的 url 映射.所以我们这里以 BeanNameUrlHandlerMapping 来进行分析.我们看 BeanNameUrlHandlerMapping 是如何查 beanName 上所有映射的 url.
到这里 HandlerMapping 组件就已经建立所有 url 和 Controller 的对应关系。 第二步、根据访问 url 找到对应的 Controller 中处理请求的方法 下面我们开始分析第二个步骤,第二个步骤是由请求触发的,所以入口为 DispatcherServlet 的核心方 法为 doService(),doService()中的核心逻辑由 doDispatch()实现,我们查看 doDispatch()的源代 码
第 2 步 :getHandler(processedRequest) 方 法 实 际 上 就 是 从 HandlerMapping 中 找 到 url 和 Controller 的对应关系.这也就是第一个步骤:建立 Map
这一部分的核心就在 2 和 4 了.先看第 2 步,通过 Request 找 Controller 的处理方法.实际上就是拼接 Controller 的 url 和方法的 url,与 Request 的 url 进行匹配,找到匹配的方法.
通过上面的代码,已经可以找到处理 Request 的 Controller 中的方法了,现在看如何解析该方法 上的参数,并调用该方法。也就是执行方法这一步。执行方法这一步最重要的就是获取方法的参数,然后 我们就可以反射调用方法了。
invocableMethod.invokeAndHandle最终要实现的目的就是:完成Request中的参数和方法参数上数 据的绑定。 SpringMVC 中提供两种 Request 参数到方法中参数的绑定方式:
① 通过注解进行绑定,@RequestParam
② 通过参数名称进行绑定. 使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将 request 中参 数 a 的值绑定到方法的该参数上.使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java 反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法.SpringMVC 解决这个问题的方 法是用 asm 框架读取字节码文件,来获取方法的参数名称.asm 框架是一个字节码操作框架,关于 asm 更 多介绍可以参考它的官网.个人建议,使用注解来完成参数绑定,这样就可以省去 asm 框架的读取字节码 的操作
关于 asm 框架获取方法参数的部分,这里就不再进行分析了.感兴趣的话自己去就能看到这个过程. 到这里,方法的参数值列表也获取到了,就可以直接进行方法的调用了.整个请求过程中最复杂的一 步就是在这里了.ok,到这里整个请求处理过程的关键步骤都分析完了.理解了 SpringMVC 中的请求处 理流程,整个代码还是比较清晰的. 5.9.4 谈谈 Spring MVC 的优化 上面我们已经对 SpringMVC 的工作原理和源码进行了分析,在这个过程发现了几个优化点: 1.Controller 如果能保持单例,尽量使用单例,这样可以减少创建对象和回收对象的开销.也就是 说,如果Controller的类变量和实例变量可以以方法形参声明的尽量以方法的形参声明,不要以类变量 和实例变量声明,这样可以避免线程安全问题. 2.处理 Request 的方法中的形参务必加上@RequestParam 注解,这样可以避免 SpringMVC 使用 asm 框架读取 class 文件获取方法参数名的过程.即便 SpringMVC 对读取出的方法参数名进行了缓存, 如果不要读取 class 文件当然是更加好. 3.阅读源码的过程中,发现 SpringMVC 并没有对处理 url 的方法进行缓存,也就是说每次都要根据 请求 url 去匹配 Controller 中的方法 url,如果把 url 和 Method 的关系缓存起来,会不会带来性能上 的提升呢?有点恶心的是,负责解析 url 和 Method 对应关系的 ServletHandlerMethodResolver 是一 个private的内部类,不能直接继承该类增强代码,必须要该代码后重新编译.当然,如果缓存起来,必须 要考虑缓存的线程安全问题