对Spring MVC Framework的理解(转)

               对Spring MVC Framework的理解

前言
最近在看 Spring MVC 的源码 , 就把自己对 MVC 模式和对各种框架的实现的认识写出来给大家看看 , 算是一个总结 . 所以 , 恳请大家用怀疑的眼光来看待这篇文章 , 假如有认识不对的地方 , 麻烦指出 .
MVCWEB应用
MVC 是什么就不用我多说了 . 对于现有较成熟的 Model-View-Control(MVC) 框架而言 , 其注意的主要问题无外乎下面这些 :
Model:
模型应该包含由视图显示的数据 . J2EE Web 应用中 , 数据通常应该由普通的 javabean 组成 . 一旦一个控制器选择了视图 , 模型就要包含视图相应的数据 . 模型本身不应该进一步的访问数据 , 也不应该和业务对象相联系 .
模型要解决的问题包括 :
l          封装要显示的数据
l          我不认为模型要依赖于特定的框架
l          不一定非得是 javabean
View:
视图负责显示出模型包含的信息 , 视图不必了解控制器或是底层业务对象的具体实现
视图要解决的问题包括 :
l          在显示给定数据模型的情况下显示内容
l          不应该包含有业务逻辑
l          可能需要执行显示逻辑 , 比如颜色交替的显示某个数组的各行
l          视图最好不处理验证的错误 , 数据的验证应该在由其他组件完成
l          视图不应该处理参数 , 参数应该交由控制器集中处理
Control:
控制器就好像 MVC 里的中枢神经 , 它也许会需要一些助手来帮助它比如解析视图 , 解析参数等 . 控制器可以访问到业务对象或者是它的代理是很重要的 , 比如 Struts 里的 Action.
控制器要解决的问题包括 :
l          检查和抽取请求参数
l          调用业务对象 , 传递从请求中获取的参数
l          创建模型 , 视图讲显示对应的模型
l          选择一个合适的视图发送给客户端
l          控制器有时不会只有一个
现有的框架
现在已经有很多的 MVC 的框架实现 . 比较流行的应该就是 Struts Webwork
Struts
这是最流行的 web 框架 , 几乎成为了实际上的工业标准 . 除了上面讨论的 MVC 模式应该有的优点以外 . 它还有如下一些缺点 :
l          每个 Action 只生成一次 , 然后就被缓存起来 , 再次请求这个 Action 的时候就不会生成新的对象 , 而是重复使用第一次生成的对象 , 这就意味着每个 Action 必须是线程安全的
l          采用 ActionForm 封装了表单数据 , 但是却只能对应 String 类型的数据 , 虽然它可以使用工具 Commons Beanutils 进行类型转化 , 但是仅仅是提供了对象级别的支持
l          严重的依赖于 Servlet API, 测试比较困难 ( 不过下一版 Struts 里的 Action.execute 的方法签名讲会换成 execute(ActionContext actionContext), 依赖也许不会那么严重 )
l          框架本身的验证规则比较简单 , 一般都是依赖于 Commons Validation 进行验证
l          想在 Action 前后做些处理很困难 . 有时甚至不得不自己去写专门的控制器
l          由于 Struts 都是具体的类继承 , 这样很容易打破封装 ?
l          提供各式各样的自定义的标签 , 但是数据绑定太原始了 , 这样就使页面代码依赖于 Struts 这个特定的框架 , 而它却不是规范 , 我觉得这是很致命的
l          它太面向 JSP , 尽管使用其他视图技术是有可能的 , 但是使用的时候却不是很方便
Webwork
这个框架虽然我没使用过 , 但是却一直在关注它的发展
 
Webwork 的设计思想采用了比 Struts 更为聪明的一种方式 , 就技术角度上说比 Struts 要高出不少 . 它以 Command 模式为基础 . 分为 Xwork Webwork, 而且框架并不依赖于 Servlet API.
 
Xwork 提供了很多核心功能 : 拦截器( Interceptor , 运行时表单验证 , 类型转换 ,IoC 容器等 .
 
WebWork 建立在 Xwork 之上 , 用于处理基于 HTTP 的响应和请求 . Map ActionContext 封装了 Session,Application 等这些 Servlet 对象 . 从而解除了和 Servlet API 的耦合 .
 
但是它仍然不是完美的 :
l          为每一个请求都创建一个 Action 可能有些浪费 .( 但是 Servlet 引擎也是为每个请求创建多个对象 , 但是也没看出来对性能有多大的影响 ?)
l          当项目越来越大的时候 , 配置文件可能会很零乱 . 好像它不支持多个配置文件
l          异常处理是 Command 模式里值得注意的问题 : 我们不知道某一特定命令可能会抛出什么特定的异常 , 所以 execute() 被迫抛出异常 , 而不论异常是运行时异常 , 还是已检查异常
 Spring MVC Framework的目标
上面说了一些 MVC 的原理 , 以及现在主流框架的一些问题 , 现在来看 Spring 是如何处理的 . Spring MVC 框架根据不同的角色定义了很多接口 , 但是它最大的问题也是依赖于 Servlet API
Spring MVC Framework 有这样一些特点 :
l          它是基于组件技术的 . 全部的应用对象 , 无论控制器和视图 , 还是业务对象之类的都是 java 组件 . 并且和 Spring 提供的其他基础结构紧密集成 .
l          不依赖于 Servlet API( 目标虽是如此 , 但是在实现的时候确实是依赖于 Servlet )
l          可以任意使用各种视图技术 , 而不仅仅局限于 JSP
l          支持各种请求资源的映射策略
l          它应是易于扩展的
我认为评价一个框架 , 应该有几个原则
l          它应该是易于使用的 , 易于测试的
Spring 易于使用吗 ? 我不这么觉得 , 尤其是它的配置文件 . 在最恐怖的情况下 , 各种业务逻辑 , 基础设施也许会拥挤在一个配置文件里 . 而如事务处理这些基础设施应该是由容器管理而不是开发人员 , 就算把这些分开到几个配置文件里 , 逻辑上虽然清晰了 , 但是基础设置却还是暴露在外边
Spring 易于测试吗 ? Spring 进行单元测试很容易 , 测试起来很方便
l          应该在多个层次上提供接口
Spring 提供了很多接口 , 而几乎每个接口都有默认的抽象实现 , 每个抽象实现都有一些具体实现 , 所以在可扩展性这点上 Spring 无疑是很优秀的
l          框架内部和框架外部应该被区别对待
框架内部可以很复杂 , 但是使用起来一定要简单 ,Spring 的内部比较麻烦 , 但是它很好的隐藏了这种复杂性 , 使用起来很舒服 , 比如设置一个 bean 的属性 . 仅仅是 setPropertyValue (String propertyName, Object value)就完成,至于怎么去设置,Spring完全隐藏了这种复杂性
l          完善的文档和测试集
这个就不用说了 , 老外的东西 , 都很完善
 Spring Web框架基本流程
知道了 Spring MVC 框架 , 现在来看看它的流程
Spring MVC Framework 大至流程如下 :
web 程序启动的时候 ,ContextLoaderServlet 会把对应的配置文件信息读取出来 , 通过注射去初始化控制器 DispatchServlet. 而当接受到一个 HTTP 请求的时候 , DispatchServlet 会让 HandlerMapping 去处理这个请求 .HandlerMapping 根据请求 URL( 不一定非要是 URL, 完全可以自定义 , 非常灵活 ) 来选择一个 Controller. 然后 DispatchServlet 会在调用选定的 Controller handlerRequest 方法 , 并且在这个方法前后调用这个 Controller interceptor( 假如有配置的话 ), 然后返回一个视图和模型的集合 ModelAndView. 框架通过 ViewResolver 来解析视图并且返回一个 View 对象 , 最后调用 View render 方法返回到客户端
DispatcherServlet
这是框架的控制器 , 是一个具体类 , 它通过运行时的上下文对象来初始化 . 控制器本身并不去控制流程 , 而只是是 Controller 控制器 ”, 他只是把处理请求的责任委托给了对应的 Controller.
 
控制器继承自抽象基类 FrameworkServlet, 它的属性 webApplicationContext 就代表着这个 web 程序上下文 , 而这个上下文对象默认实现就是从一个 XML 文件读取配置信息 ( 当然也可以是其他文件格式 ). WebApplicationContext 其实是 beans 包的东西 , 这个包提供了这个 Spring 整个框架的基础结构 , 以后我会分析这个包的内容 . 但是现在仅仅需要知道 WebApplicationContext 代表一个 web 应用的上下文对象 .
 
现在来看看 DispatchServlet 是如何工作的 :
DispatchServlet 由于继承自抽象基类 FrameworkServlet, FrameworkServlet 里的 doGet(),doPost() 方法里有调用 serviceWrapper(), 跳到 serviceWrapper() 里去看 , 结果发现它有把具体实现委托给了 doService(request, response); 方法 . 所以现在已经很清楚了 , DispatchServlet 真正实现功能的是 doService() 这个方法 .
 
特别的 , FrameworkServlet initFrameworkServlet() 这个方法是控制器的初始化方法 , 用来初始化 HandlerMappings 之类的对象 , 这也是延迟到子类实现的 . 其实就是一个 Template 模式的实现 .don’t call us, we will call u. 总的看来 ,Spring 就是通过这样来实现它的控制反转的 : 用框架来控制流程 , 而不是用户
 
跳到 doService() 一看究竟 , 就会发现真正工作的又是另一个助手函数 doDispatch(request, response), 没办法 , 继续看下去 , 发现这样两行代码
HandlerExecutionChain mappedHandler = null;
         mappedHandler = getHandler(processedRequest, false);
HandlerExecutionChain 源码就发现它其实就是对 Controller 和它的 Interceptors 的进行了包装 ;
 
getHandler() 就是从 HandlerMappings( 这是一个 List, 存放的 handlerMapping 对象 ) 中取出对应的 handlerMapping 对象 , 每个 HandlerMapping 对象代表一个 Controller URL 的映射 ( 其实在运行的时候是一个 HandlerExecutionChain URL 的映射 , HandlerExecutionChain 对象其实就是对 Controller 和它 interceptors 的一个包装器 , 可以把 HandlerMapping 看成 Controller URL 的映射 ). 而这个 HandlerMapping 是通过配置文件在运行时注射进来的 , 一般是 SimpleUrlHandlerMapping 这个子类
 
取得了 HandlerMapping 对象 , 继续向下看 , 发现 :
                  if (mappedHandler.getInterceptors() != null) {
                                               for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
                                                        HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
                                                        if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                                                                 triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                                                                 return;
                                                        }
                                                        interceptorIndex = i;
                                               }
                                     }
这里就是在调用 Controller 的拦截器 , 原理就是这句了 :
         interceptor.preHandle(processedRequest, response, mappedHandler.getHandler(), mv);
preHandle 方法传入了 mappedHandler.getHandler() 这个参数来实现递归调用 ! interceptor.postHandle 方法如此一般 . 只不过这个方法是在 handleRequest 方法后调用
 
继续看下去 :
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
发现 Controller handleRequest 真正的操作又被代理给了 HandlerAdapter handle 方法 , 并且返回一个 ModelAndView, 我想这里增加一层的意义应该是为了解除 Controller DispatchServlet 的耦合吧 .
 
接着就很简单了 , 调用 render() 方法 , 在这个方法里面由 ViewResoler 解析出视图名 , 再调用视图对象的 render 方法把合适的视图展现给用户
 
到此 , 控制器的流程就 OVER
HandlerMapping
通过使用 HandlerMapping, 控制器可以用 URL 和某一个 Controller 进行标准的映射 , 而实现 URL 映射的具体子类的 UrlHandlerMapping.
 
Spring 还允许我们自定义映射 , 比如通过 Session,cookie 或者用户状态来映射 . 而这一切仅仅只需要实现 HandlerMapping 接口而已 . 不过 URL 映射已经能满足大部分的要求
Controller
Controller 类似 Structs Action, Controller 接口只有一个方法 handleRequest(), 放回一个 ModelAndView 对象 , 如同设计目标所说的那样 , 每个 Controller 都是一个 java 组件 , 所以它可以在上下文环境中任意配置 , 组件属性都会在初始化的时候被配置 .Spring 自己提供了几个具体的实现 . 方便我们使用
ViewResolver
Controller 通常返回包含视图名字而不是视图对象的 ModelAndView 对象 . 从而彻底的解除了控制器和视图之间的耦合关系 , 并且在这里还可以提供国际化的支持 .
在你的配置文件中你可以 :
welcomeView.class = org.springframework.web.servlet.view. InternalResourceView
welcomeView.url=/welcome.jsp
也可以
welcomeView.class = org.springframework.web.servlet.view.xslt. XsltView
welcomeView.url=/xslt/default.xslt
 
View
这也是一个 java 组件 , 它不做任何请求处理或是业务逻辑 , 它仅仅获取模型传递的数据 , 并把数据显示出来 . 它里面的 render 方法按照如下流程工作 :
l          设置模型的数据到 request 作用域
l          取得视图的 URL
l          转发到对应的 URL
总结:
Spring web 框架是一个很优秀的框架 , 在这里只是走马观花的分析了 Spring 的工作流程和一些关键的类 , 但是并没有去深入的去探讨它背后所体现的思想 , 还有它的优缺点等东西 . 这些都等下次再说吧
 
这里再次说明一下 , 上面说的只是我自己的想法 , 假如有不理解或者不正确的地方 , 请在下面留言指出或者查看官方的 <Spring-Reference>, 以后会逐渐推出自己对 Spring 其他的理解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值