内容一览:
-
对 SpringMVC 执行流程的一个概述
-
介绍在搭建 SpringMVC 开发环境时三种常用的处理器映射器(HandlerMapping)和处理器适配器(HandlerAdapter):
-
BeanNameUrlHandlerMapping 与 SimpleControllerHandlerAdapter
-
SimpleUrlHandlerMapping 与 HttpRequestHandlerAdapter
-
RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter
SpringMVC 执行流程
先附上一张 SpringMVC 的执行流程图:
更加详细的一幅图:
- 客户端发来一个请求。
- 前端控制器(DispatcherServlet)拦截到这个请求加载相应的配置文件,通过 HandlerMapping(处理器映射器) 去查找该请求是否有对应的 Handler(就是我们写的那个 Controller类,这里专业术语叫 Handler) 来处理。
- 如果有就通过 HandlerAdapter(处理器适配器)去调用这个 Handler 来处理请求
- 适配器返回一个 ModelAndView(数据 + 视图名)。
- 前端控制器通过 ViewResolver(视图解析器)把返回的 ModelAndView 中的 View 解析成一个视图(其实就是页面)。
- 前端控制器将 Model(数据)渲染到 View(视图)。
- 返回给客户端响应。
可能大家看完上面有点偏专业性的解释还是不太懂,所以我就用一个形象的例子来解释一下:
比方说我要去某公司现场面试。那么我去面试这就相当于发起了一个请求,面试总得有前台接待人员吧,这个 DispatcherServlet 就相当于一个前台接待人员。于是,我走到前台接待的小姐姐跟前说:我要面试 xxx 岗位。小姐姐哪记得住公司有没有安排你来面试啊,所以,小姐姐就问你叫什么名字?“我叫 xxx ”,“哦,稍等,我先查一下面试表”,这个面试表就相当于一个 HandlerMapping,它存储了面试者与对应的面试官之间的映射关系。然后她就拿着那张表找到了面试我的面试官(这里的面试官就相当于 Handler,是用来处理我这个面试请求的),然后就通知对应的面试官来面试我。但是,不同的面试官的风格可能不同,有的面试官就希望接待人员能通过打电话的方式来通知他去面试,而有的面试官就有可能喜欢接待人员发个邮件给他来通知他去面试。但是接待小姐姐怎么忙的过来呢?又要去为来应聘的面试者查找对应的面试官,还要通知对应的面试官来面试应聘者,还要以不同的方式来通知,好累的。所以小姐姐就找了几个小哥哥(HandlerAdapter)来帮她以不同的方式来通知面试官来面试。通知到了对应的面试官之后,面试官就开始面试应聘者(处理请求),面试结束之后,面试官就写评语,给一些反馈信息(ModelAndView)。然后再由专门处理面试反馈信息的人员(ViewResolver)来根据反馈信息来评估该面试者(对应图中的第 7 步,将数据渲染到视图),最后,评估结束,给面试者发条短信(响应),通过面试就获得 offer,没通过,下次继续努力!
相信看到这里,大家对 SpringMVC 执行流程的理解又进了一步,不至于那么抽象了。那接下来我们就来讨论一下执行流程中的一些细节:
- 接待的小姐姐是通过一张表(HandlerMapping)来确认面试请求是否有对应的面试官(Handler)来处理,那么到底有多少种 HandlerMapping 呢?
- 接待的小姐姐是通过小哥哥(HandlerAdapter)来通知不同风格的面试官来面试应聘者的,那么又有多少种 HandlerAdapter 呢?
BeanNameUrlHandlerMapping 与 SimpleControllerHandlerAdapter
BeanNameUrlHandlerMapping:看名字应该可以猜出来它是通过 bean 的 name 来确定请求所对应的处理器(Handler),需要在注册的 Handler 的 bean 标签上 配置 name 属性为对应的所要处理的请求。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置处理器映射器(面试表) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- 配置处理器适配器(小哥哥) -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 配置视图解析器(面试结果评估人员) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
<!-- 注册我们实现的Controller(面试官),该面试官只能处理网站根目录下的 login.do 的请求 -->
<bean name="/login.do" class="com.lyu.qjl.interview.controller.LoginController"></bean>
</beans>
这种方式缺点就很明显了:一个面试官只能处理一个面试请求,因为一个 bean 只能配一个 name 属性。
SimpleControllerHandlerAdapter:如果使用这种处理器适配器来调用 Handler 的话,对应的 Handler 要求必须实现 Controller 接口,重写 handlerRequest 方法,并在配置文件中注册。
上面注册的 LoginController 的源码如下:
package com.lyu.qjl.interview.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
/**
* 类名称:用于处理登录请求的处理器
* 全限定性类名: com.lyu.qjl.interview.controller.LoginController
* @author 曲健磊
* @date 2018年5月21日下午7:02:03
* @version V1.0
*/
public class LoginController implements Controller {
@Override
public ModelAndView handleRequest