Spring MVC 是什么
Spring MVC属于SpringFrameWork的后续产品,是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。
Spring MVC 架构
Spring MVC 的核心处理请求流程如下图:
注:为了更好的从总体上把握请求流程,上图对请求流程做了很多简化,忽略了处理器返回结果和前端控制器返回结果的中更多的处理过程。
1.用户发送的请求交给统一的
一个前端控制器
。
2.前端控制器接收到请求,根据请求信息(如URL)从
多个页面控制器
中选择相匹配的一个,将请求委托给其处理。
3.页面控制/处理器接收到请求后进行相应的业务处理,并最终将处理结果返回给前端控制器。
4.
前端控制器
根据处理器返回的结果获取视图并对视图进行渲染,最终得到处理结果。
5.前端控制器将最终的结果返回给用户。
前端控制器
在Spring MVC中前端控制器是DispatcherServlet
这是一个servlet,那么事情就很明显了——用户发送请求,servlet收到请求,servlet处理请求(业务逻辑分派给具体的处理器处理),servlet返回处理结果。
问题1:
用户的的请求是如何给到Spring MVC的前端控制器(DispatcherServlet)的?
答案在web.xml配置文件中:
在使用Spring MVC时,我们需要在web.xml中配置一个serlvet,这个servlet就是前端控制器DispatcherServlet。
load-on-startup
:表示启动容器时初始化该Servlet;
url-pattern
:表示哪些请求交给DispatcherServlet(Spring MVC)处理, “/” 是用来定义默认servlet映射的。
进行这样的配置后,web容器就将所有符合要求的请求交给了DispatcherServlet也就是Spring MVC来处理。
问题2:
DispatcherServlet是如何具体处理接收到的请求的?
DispatcherServlet核心处理流程:
DispatcherServlet核心代码(spring-webmvc-4.1.4):
当DispatcherServlet接收到请求后首先进入doService方法,然后调用核心分派方法doDispatch
doService代码:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 保存现场,以便能在必要时恢复。
// 2. 将框架需要的对象放入request中,以便view和handler使用。
try {
// 3. 请求分派.
doDispatch(request, response);
} finally {
// 4. 恢复现场。
}
}
接下来着重看下doDispatch方法的代码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 获取WebAsyncManager
// WebAsyncManager是 异步处理的入口类 同时对Callables和DeferredResults启动的管理
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查请求是否是multipart(如文件上传)
processedRequest = checkMultipart(request);
// 如果是multipart那么MultipartResolver会根据request重新构建一个请求,此处判断是否为原请求
multipartRequestParsed = (processedRequest != request);
// 通过HandlerMapping匹配某个具体的页面控制器/处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 使用适配器,将匹配到的的处理器包装成相应的适配器,以支持更多种类型的处理器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// last-modified 缓存支持
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 执行处理器相关的拦截器的预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 适配器调用处理器的功能处理方法,返回一个ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
// 执行处理器相关的拦截器的后置处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
// 由ViewResolver解析渲染View,渲染时会把Model传入。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
解析渲染view代码:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
// 省略代码
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// 省略代码
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 省略代码
// 获取视图(View)
View view;
if (mv.isReference()) {
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName()
+ "' in servlet with name '" + getServletName() + "'");
}
} else {
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a "
+ "View object in servlet with name '" + getServletName() + "'");
}
}
// 省略代码
try {
// 使用Model渲染视图
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
// 省略代码
}
}
1、 收到请求。首先用户发送请求给DispatcherServlet(前端控制器),前端控制器收到请求后自己不进行处理,而是委托给其他的处理器进行处理,作为统一访问点,进行全局的流程控制;
2、 得到处理器。DispatcherServlet接收到请求,根据HandlerMapping将请求映射为HandlerExecutionChain对象(包含一个Handler处理器、多个HandlerInterceptor拦截器);
3、 处理器处理请求。HandlerAdapter将HandlerExecutionChain对象中的Handler处理器包装为适配器,然后通过适配器调用处理器功能处理方法真正的处理请求,并返回一个ModelAndView对象(包含模型数据和视图/视图名);
5、 得到视图。如果ModelAndView对象中包含的是逻辑视图名则调用ViewResolver将把逻辑视图名解析为具体的View,如果ModelAndView对象中包含的已经是一个View则直接返回该View;
6、 渲染视图。View根据Model模型数据进行渲染,此处的Model实际是一个Map数据结构;
7、 返回结果。DispatcherServlet返回响应给用户。
Spring MVC 主要组件
经过上面的流程分析,Spring MVC的主要组件也就比较清晰了:
1、前端控制器 DispatcherServlet
作用:统一访问点,进行全局的流程控制,有了DispatcherServlet 就减少了其它组件之间的耦合度
2、处理器映射器HandlerMapping
作用:根据请求的URL来查找Handler
3、处理器Handler(
需要程序员开发
)
作用:具体处理用户请求
4、处理器适配器HandlerAdapter
作用:按照特定的规则(HandlerAdapter要求的规则)去执行Handler。适配器设计模式的应用,从而很容易支持很多类型的处理器。
5、视图解析器 ViewResolver
作用:进行视图的解析 根据逻辑视图名解析成真正的视图(View)
6、视图View(
需要程序员开发
)
View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,velocity……)
Spring MVC的优点
1.清晰的角色划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、处理器或页面控制器(Controller)、验证器(Validator)、命令对象(Command Object)、表单对象(Form Object)。每一个角色都可以由一个专门的对象来实现。
2.强大而直接的配置方式:将框架类和应用程序累都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器validator)的引用。
3.可适配、非侵入:可以根据不同的应用场景,选择何事的控制器子类(simple型、command型、from型、wizard型、multi-action型或者自定义),而不是一个单一控制器(比如Action/ActionForm)继承。
4.可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
5.可定制的绑定(binding)和验证(validation):比如将类型不匹配作为应用级的验证错误,这可以保证错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象,需要手动解析它并转换到业务对象。
6.可定制的handler mapping和view resolution:Spring提供从最简单的URL映射,到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。
7.灵活的model转换:在Springweb框架中,使用基于Map的键/值对来达到轻易的与各种视图技术集成。
8.可定制的本地化和主题(theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity、支持Freemarker(不需要额外的中间层)等等。
9.简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(theme)之类的许多功能。他提供在标记方面的最大灵活性。
10.JSP表单标签库:在Spring2.0中引入的表单标签库,使用在JSP编写表单更加容易。