6.2.1. 鸟瞰SpringMVC
SpringMVC框架在处理控制器(Controller)的实现方面与其他的request驱动的web框架在总体思路上是相似的,
就跟我们所说的那样,通过引入Front Controller和Page
Controller的概念来分离流程控制逻辑与具体的web请求处理逻辑。
org.springframework.web.servlet.DispatcherServlet就是SpringMVC框架中的Front
Controller,他负责接收并处理所有的web请求,只不过具体的处理逻辑他会委派给他的下一级控制器实现,即
org.springframework.web.servlet.mvc.Controller,
而org.springframework.web.servlet.mvc.Controller[
]
则对应Page Controller的角色定义:
当然,仅仅有他们两个还远远称不上一个完整的web开发框架,要使得整个的web开发框架能够运作起来,我们还需要更多的角色相助!下面,让我带您走过DispatcherServlet这道长廊,并乘机一一为您介绍SpringMVC当中的各位“掌权者
”。
在我们开始DispatcherServlet的旅程之前,我们不妨先回顾一下通常的控制器Servlet都会做哪些工作吧!
就以我们JSP Model 2架构中提到的MockMagicServlet为例(之前我特意请您关注过它),
对于它在处理web请求的过程中所做的工作,我们可以简单归纳为三点:
-
获取请求信息,比如请求的路径,各种参数值等等;
String parameter1 = request.getParameter("paramName1");
String parameter2 = request.getParameter("paramName2");
// ...
-
根据请求信息调用具体的service对象处理具体的web请求;
List<InfoBean> infoList = MockServletService.query(parameter1,parameter2);
-
处理完成后,将要显示给视图的模型数据通过request传递,然后通过RequestDispatcher选择具体的jsp视图显示;
request.setAttribute("infoList", infoList);
forward(request,response,"view.jsp");
现在让我们再看DispatcherServlet在整个web请求处理过程中所做的事情,你将发现,总的原则上并没有太多改变,唯一改变的是,DispatcherServlet将各项工作细化并分离给了较为独立的角色来完成。
Procedure 6.1.
DispatcherServlet处理流程概略[
]
-
HandlerMapping先生(web请求的处理协调人)
既然DispatcherServlet是整个框架的FrontController,当我们将它注册到web.xml的时候,就注定了它要服务于规定的一组web请求,而不是单独的一个web请求:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
不能够像“一个web请求对应一个servlet
”那样获取web容器对URL映射匹配的支持,我们的DispatcherServlet现在只好自己来处理具体的web请求与具体的处理类之间的映射关系匹配了。
对于web请求与具体的处理类之间的映射匹配来说,具体的处理方式或者说策略可能多种多样,完全可能随着应用程序甚至每一个具体的web请求的不同而发生变化:
-
最常见的,就是那种“掐头去尾
”的处理方式,将web请求的URL路径去除前面的上下文路径(Context path)和最后的扩展名,取最终剩下的路径信息作为匹配的结果,比如:
http://www.nosuchname.com/yourapp/resource.html
最终以resource
作为匹配结果。
-
以web请求的URL中存在的某个参数的值作为匹配的标准,比如:
http://www.nosuchname.com/yourapp/resource.html?controller=yourController
当发现请求的路径中存在controller这个参数的话,其值就会被作为匹配结果用来调用具体的处理类,在这里是yourController。
-
以cookie或者session中的某些信息作为匹配标准,比如针对某一个客户的web请求全部转发给单独的一个处理类进行处理。
甚至于,结合Ruby On
Rails的理念,我们在开发中规定一些的惯例(Convention),然后以这些惯例解析web请求的URL路径信息以获取具体的处理类匹配。
可见,要将映射匹配的逻辑写死到DispatcherServlet是无法有效扩展的,而且,匹配的方式也可能随着需求而变化,所以,SpringMVC
为了能够灵活的处理映射的匹配,
引入了org.springframework.web.servlet.HandlerMapping专门管理web请求到具体的处理类之间的映射关
系。
当web请求到达DispatcherServlet之后,Dispatcher将寻求具体的HandlerMapping实例以获取对应当前web请求
的具体处理类(即org.springframework.web.servlet.Controller)。
-
org.springframework.web.servlet.Controller(web请求的具体处理者)
org.springframework.web.servlet.Controller是对应DispatcherServlet的次级控制器
(sub-controller),它本身实现了对应某个具体web请求的处理逻辑。当你所使用的HandlerMapping查找到当前web请求对应
哪一个org.springframework.web.servlet.Controller具体实例之后,
DispatcherServlet即可获得HandlerMapping所返回的结果,并调用
org.springframework.web.servlet.Controller的处理方法来处理当前的web请求。
org.springframework.web.servlet.Controller的处理方法执行完毕之后将返回一个
org.springframework.web.servlet.ModelAndView实例,ModelAndView包含了两部分信息:
有了ModelAndView所包含的视图与模型二者的信息之后,DispatcherServlet就可着手视图的渲染工作了。
-
ViewResolver和View(视图独立战争的领导者)
按照MockMagicServlet的处理流程,我们已经走到了最后一步,即选择并转到最终的JSP视图文件:
request.setAttribute("infoList", infoList);
forward(request,response,"view.jsp");
但是,对于一个web框架来说,我们是不可以这么简单的处理的,为什么那?不要忘了,现在可用的视图技术可不仅仅JSP一
家,Velocity,Freemarker等通用的模板引擎都可以帮助我们构建相应的视图,
而他们是不依赖于request对象来传递模型数据的,甚至于,我们也不仅仅依赖于JSP专用的RequestDispatcher来输出最终的视图,否
则,我们也没有必要通过ModelAndView来返回视图以及模型数据了,直接在
org.springframework.web.servlet.Controller内部完成视图的渲染工作就可以了,不是吗?鉴于此,Spring
提出了一套基于ViewResolver和View接口的web视图处理抽象层, 以屏蔽web框架在使用不同的web视图技术时候的差异性。
那么,SpringMVC是如何以统一的方式将相同的模型数据纳入不同的视图显示的那?
实际上,撇开JSP使用的RequestDispatcher不论,servlet自身就提供了两种最基本的视图输出方式,不知道您还是否记得我们最初的out.println?!
基本来说,我们要向客户端输出的视图类型可以分为“文本
”以及“二进制
”两种方式,比如JSP/JSTL,Velocity/Freemarker等最终的输出结果都是以(X)HTML等标记文本形式表现的,而PDF/Excel之类则属于二进制内容行列。
对于这两种形式的视图内容的输出,Servlet自身所暴露给我们的HttpServletResponse已经足够应付:
// markup text view output with servlet
String markupText = ...;
PrintWriter writer = response.getWriter();
writer.write(markupText);
writer.close();
...
// binary view output with servlet
byte[] binaryContext = ...;
ServletOutputStream out = response.getOutputStream();
out.write(binaryContext);
out.flush();
out.close();
在HttpServletResponse可以同时支持文本形式以及二进制形式的视图输出的前提下,我们只要在最终将视图数据通过HttpServletResponse输出之前,
借助于不同的视图技术API,结合模型数据和相应的模板文件生成最终的视图结果:
1 获取模型数据Model;
2 获取视图模板文件(比如*.jsp, *.vm, *.fm, *.xls,etc.);
3 结合视图模板和模型数据,使用相应的视图技术API生成最终视图结果;
4 将视图结果通过HttpServletResponse输出到客户端;
5 DONE!
这样,不管最终生成的视图内容如何,我们都可以以几乎相同的方式输出他们。但唯一的问题在于,我们不可能将每一个视图的生成代码都纳入DispatcherServlet的职权范围,
毕竟每一种视图技术的生成代码是不同的,而且,所使用的视图技术也可能随着具体环境而变化。
SpringMVC通过引入org.springframework.web.servlet.View接口定义来统一的抽象视图的生成策略,
这样,DispatcherServlet只需要根据Spring Controller处理完毕后通过ModelAndView返回的逻辑视图名称查找到具体的View实现,然后委派该具体的View实现来根据模型数据输出具体的视图内容即可:
现在,视图模板与模型数据的合并逻辑以及合并后的视图结果的输出逻辑全部封装到了相应的View实现类中,DispatcherServlet只需要根据
ModelAndView返回的信息选择具体的View实现类做最终的具体工作即可。
不过,与HandlerMapping帮助DispatcherServlet来查找具体的Spring
Controller以处理web请求类似,DispatcherServlet现在需要依赖于某一个
org.springframework.web.servlet.ViewResolver来帮他处理逻辑视图名与具体的View实例之间的映射对应关
系,
ViewResolver将根据ModelAndView中的逻辑视图名查找相应的View实现类,然后将查找的结果返回
DispatcherServlet,DispatcherServlet最终将ModelAndView中的模型数据交给返回的View处理最终的视图
渲染工作。
至此,整个DispatcherServlet的处理流程即告结束。
如果你还不足以从以上流程概略中分出哪一个角色对应什么样的工作,那下面这幅Sequence图或许可以帮你加深一下印象:
当我们了解了SpringMVC中的基本概念之后,让我们开始着手开发我们的第一个基于SpringMVC的web应用程序...
分享到:
相关推荐
Zynaptiq 宣布用于 Mac OS X 上 AudioUnits(AU)格式的 UNVEIL 和 PITCHMAP 插件的更新现已推出。 UNVEIL 是一款实时的去混音插件,便于用户移除或隔离包括单声道在内的任意通道数量混合信号的混响声,当前最新...
图片懒加载
《前端项目:unveil2.zip——轻量级jQuery图片懒加载插件解析》 在当前互联网技术高速发展的时代,前端开发面临着优化网页性能、提高用户体验的重任。在这个背景下,"unveil2"作为一个非常轻量级的jQuery插件...
包括Vec2Face Unveil Human Faces from their Blackbox Features inFace Recognition论文和文献阅读报告
2. **初始化unveil**:在DOM加载完成后,使用`$("img").unveil();`初始化unveil.js,选择器可以针对页面上的所有图片或其他需要延迟加载的元素。 3. **设置占位符**:为防止图片未加载时出现空白,可以在图片标签中...
在unveil-master这个项目中,很可能包含了一个实现滑动加载的示例代码或框架,包括JavaScript文件、CSS样式表和可能的HTML模板。通过研究这些文件,开发者可以学习如何在自己的项目中实现滑动加载功能,提升网页性能...
"unveil:响应式Javascript演示库"是一个专注于创建响应式和交互式演示的JavaScript库。这个库的主要目标是帮助开发者和设计师制作出引人入胜、适应不同屏幕尺寸的在线内容展示。通过使用unveil,你可以创建出在桌面...
Unveil Rs是从markdown文件创建演示文稿的工具。 它的灵感来自 , 和 。 它是什么样子的 ? 观看。 安装 来自crates.io 目前,仅在提供。 首先,您需要安装rust,然后在终端中键入以下命令: cargo install ...
use unveil :: unveil; fn main () { let path = "public.txt" ; let contents = b"Hello world!" ; File :: create (path). unwrap (). write_all (contents). unwrap (); // Restrict filesystem view by only...
React揭开面纱 轻巧且可自定义的React组件,可通过限制内容高度...演示版安装npm install --save react-unveil用法import Unveil from 'react-unveil' ;class Demo { render ( ) { return ( < Unveil maxHeight = {
Unveil.js for Rails 在轨道上延迟加载图像。 如果您想快速使用带有rails的 ,现在可以使用它。 通过加载显示在视口中而不是加载的图像,可以提高Rails应用程序的页面速度。 安装 将Gemfile -rails gem添加到您的...
具体实现这一功能,通常会使用到一些JavaScript库,比如jQuery的一个插件——`jquery.unveil.js`或其压缩版本`jquery.unveil.min.js`。这个插件的工作原理是监听滚动事件,当图片进入视口时,触发加载。使用方法通常...
`jquery.unveil.js` 是一个轻量级的 jQuery 图片懒加载插件,它的主要功能包括: 1. **自动绑定**:无需手动为每个图片元素添加事件监听器,插件会自动为所有带有`data-src`属性的图片元素进行绑定。 2. **阈值...
2. **词汇理解**:文章中出现了如“blueprints”(蓝图)、“unveil”(公布)、“drama”(传奇)、“maiden voyage”(首航)等专业或高级词汇,学生需要理解这些词汇在上下文中的含义,以便准确理解文章内容。...
in particular, our intent is to detect and unveil a possible hidden structure and regularity patterns associated with their generation mechanism. This information in turn helps our analysis and ...
Then they unveil a comprehensive Java/Ajax toolkit. Tools include JSEclipse for code editing, Venkman for JavaScript debugging, and Dojo Compressor for code compression. They also explain Log4js (and...
Delve into the internal architecture of T-SQL—and unveil the power of set-based querying—with comprehensive reference and advice from a highly regarded T-SQL expert and members of Microsoft's SQL ...
The goal of this tutorial is to progressively unveil our language, describing how it is built up over time. This will let us cover a fairly broad range of language design and LLVM-specific usage ...