spring 源码解析(spring-mvc模块)

一 Spring MVC 请求处理流程 引用 Spring in Action 上的一张图来说明了 SpringMVC 的核心组件和请求处理流程:
spring 源码解析(spring-mvc模块)_第1张图片
①: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 中.Tomcat 启动时会通知 Spring 初始化容器(加载 Bean 的定义信息和初始化所有单例 Bean),然后 SpringMVC 会遍历容器中的 Bean,获取每一个 Controller 中的所有方法访问的 url,然后将 url 和 Controller 保存到一个 Map 中; 这样就可以根据 Request 快速定位到 Controller,因为最终处理 Request 的是 Controller 中的 方法,Map 中只保留了 url 和 Controller 中的对应关系,所以要根据 Request 的 url 进一步确认 Controller 中 的 Method, 这 一 步 工 作 的 原 理 就 是 拼 接 Controller 的 url(Controller 上 @RequestMapping 的值)和方法的 url(Method 上@RequestMapping 的值),与 request 的 url 进行匹 配,找到匹配的那个方法; 确定处理请求的Method后,接下来的任务就是参数绑定,把Request中参数绑定到方法的形式参数 上,这一步是整个请求处理过程中最复杂的一个步骤。SpringMVC 提供了两种 Request 参数与方法形参 的绑定方法: ① 通过注解进行绑定,@RequestParam ② 通过参数名称进行绑定. 使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将 Request 中参 数 a 的值绑定到方法的该参数上.使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java 反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法.SpringMVC 解决这个问题的方 法是用 asm 框架读取字节码文件,来获取方法的参数名称.asm 框架是一个字节码操作框架,关于 asm 更 多介绍可以参考它的官网。个人建议,使用注解来完成参数绑定,这样就可以省去 asm 框架的读取字节码 的操作
三 Spring MVC 源码分析
我们根据工作机制中三部分来分析 SpringMVC 的源代码.。 其一,ApplicationContext 初始化时建立所有 url 和 Controller 类的对应关系(用 Map 保存); 其二,根据请求 url 找到对应的 Controller,并从 Controller 中找到处理请求的方法; 其三,request 参数绑定到方法的形参,执行方法处理请求,并返回结果视图. 第一步、建立 Map的关系 我们首先看第一个步骤,也就是建立 Map关系的部分.第一部分的入口类为 ApplicationObjectSupport 的 setApplicationContext 方法.setApplicationContext 方法中核 心 部 分 就 是 初 始 化 容 器 initApplicationContext(context), 子 类 AbstractDetectingUrlHandlerMapping 实现了该方法,所以我们直接看子类中的初始化容器方法。
在这里插入图片描述
spring 源码解析(spring-mvc模块)_第2张图片
determineUrlsForHandler(String beanName)方法的作用是获取每个 Controller 中的 url,不同 的子类有不同的实现,这是一个典型的模板设计模式.因为开发中我们用的最多的就是用注解来配置 Controller 中 的 url,BeanNameUrlHandlerMapping 是 AbstractDetectingUrlHandlerMapping 的子类,处理注解形式的 url 映射.所以我们这里以 BeanNameUrlHandlerMapping 来进行分析.我们看 BeanNameUrlHandlerMapping 是如何查 beanName 上所有映射的 url.
在这里插入图片描述
spring 源码解析(spring-mvc模块)_第3张图片
到这里 HandlerMapping 组件就已经建立所有 url 和 Controller 的对应关系。 第二步、根据访问 url 找到对应的 Controller 中处理请求的方法 下面我们开始分析第二个步骤,第二个步骤是由请求触发的,所以入口为 DispatcherServlet 的核心方 法为 doService(),doService()中的核心逻辑由 doDispatch()实现,我们查看 doDispatch()的源代 码
spring 源码解析(spring-mvc模块)_第4张图片
spring 源码解析(spring-mvc模块)_第5张图片
spring 源码解析(spring-mvc模块)_第6张图片
第 2 步 :getHandler(processedRequest) 方 法 实 际 上 就 是 从 HandlerMapping 中 找 到 url 和 Controller 的对应关系.这也就是第一个步骤:建立 Map的意义.我们知道,最终处 理 Request 的 是 Controller 中 的 方 法 , 我 们 现 在 只 是 知 道 了 Controller, 还 要 进 一 步 确 认 Controller 中处理 Request 的方法.由于下面的步骤和第三个步骤关系更加紧密,直接转到第三个步 骤. 第三步、反射调用处理请求的方法,返回结果视图 上面的方法中,第 2 步其实就是从第一个步骤中的 Map中取得 Controller,然后经过 拦截器的预处理方法,到最核心的部分–第 5 步调用 Controller 的方法处理请求.在第 2 步中我们可以 知道处理 Request 的 Controller,第 5 步就是要根据 url 确定 Controller 中处理请求的方法,然后 通过反射获取该方法上的注解和参数,解析方法和参数上的注解,最后反射调用方法获取 ModelAndView 结果视图。因为上面采用注解 url 形式说明的.第 5 步调用的就是 RequestMappingHandlerAdapter 的 handle()中的核心逻辑由 handleInternal(request, response, handler)实现。
spring 源码解析(spring-mvc模块)_第7张图片
spring 源码解析(spring-mvc模块)_第8张图片
这一部分的核心就在 2 和 4 了.先看第 2 步,通过 Request 找 Controller 的处理方法.实际上就是拼接 Controller 的 url 和方法的 url,与 Request 的 url 进行匹配,找到匹配的方法.
spring 源码解析(spring-mvc模块)_第9张图片
spring 源码解析(spring-mvc模块)_第10张图片
通过上面的代码,已经可以找到处理 Request 的 Controller 中的方法了,现在看如何解析该方法 上的参数,并调用该方法。也就是执行方法这一步。执行方法这一步最重要的就是获取方法的参数,然后 我们就可以反射调用方法了。
spring 源码解析(spring-mvc模块)_第11张图片
spring 源码解析(spring-mvc模块)_第12张图片
invocableMethod.invokeAndHandle最终要实现的目的就是:完成Request中的参数和方法参数上数 据的绑定。 SpringMVC 中提供两种 Request 参数到方法中参数的绑定方式:
① 通过注解进行绑定,@RequestParam
② 通过参数名称进行绑定. 使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将 request 中参 数 a 的值绑定到方法的该参数上.使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java 反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法.SpringMVC 解决这个问题的方 法是用 asm 框架读取字节码文件,来获取方法的参数名称.asm 框架是一个字节码操作框架,关于 asm 更 多介绍可以参考它的官网.个人建议,使用注解来完成参数绑定,这样就可以省去 asm 框架的读取字节码 的操作
在这里插入图片描述
spring 源码解析(spring-mvc模块)_第13张图片
关于 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的内部类,不能直接继承该类增强代码,必须要该代码后重新编译.当然,如果缓存起来,必须 要考虑缓存的线程安全问题

你可能感兴趣的:(spring)