在当今(的)MVC framework里,,似乎Webwork2逐渐成为主流,, Webwork2+SpringFramework(的)组合变(得)越来越流行..这似乎意味着Spring自带(的)MVC framework远比Webwork2差,,所以大家纷纷用Webwork2来代替..确实,,Spring(的)MVC framework不算是整个Spring(的)核心部件,,但它们(的)威力却超过了很多人(的)想象..很多人包括xiecc认为 Spring(的)MVC framework是非常优秀(的),,甚至比Webwork2更优秀..
下面列举一下 Spring(的)MVC framework在设计时做出(的)一些重要(的)决定,,并将之和相关(的)MVC framework如Webwork2或struts进行对比:
一、Spring(的)整个MVC配置是基于IOC容器(的)
与 struts或webwork2相比,,这是一个ms有点奇怪(的)决定,,看一下Spring MVC(的)配置文件,,最先看到(的)不是action或者form,,而是一些有着特定名字(的)bean,,Bean下面(的)配置是一些简单或有点复杂(的)属性..我们们看到(的)是机器更容易(的)数据结构,,而不是人更容易理解(的)元素..
但是这恰恰是 Spring(的)MVC强大(的)根源!因为它们(的)配置就是Spring(的)核心IOC容器(的)配置,,这意味着所有IOC容器(的)威力都可以在这里展现,,我们们可以为所欲为(地)对Spring MVC进行扩展和增强,,我们们可以完成在其它们MVC framwork中很多难以想象(的)任务..想扩展新(的)URL映射方式吗?要换一个themeResolver或LocalReolver(的)实现吗?想在页面中显示新类型(的)View(比如说RDF,,呵呵,,一个小秘密:xiecc是研究语义网(的),,虽然成天不务正业,,不写论文,,只写八卦)?甚至想直接在Controller里定义AOP吗?这些对Spring(的)MVC来说都是小菜一碟..
我们没有仔细研究过 Webwork2(的)扩展机制,,我们知道通过Webwork2(的)interceptor机制,,可以进行很多(的)扩展,,甚至有一个简单简单 (的)IOC容器..但不管它们有多强大,,提供了多少扩展点..它们(的)威力都很难和真正(的)IOC容器相比..而struts(的)plugin 功能则是出名(的)滥,,虽然它们也提供了plugin机制..
Spring采用IOC配置(的)另一个原因是使 Spring(的)MVC与Spring(的)IOC容器(的)整合变(得)非常(的)容易..Spring提供了与struts与 webwork2(的)整合,,但是这样整合都需要在进行间接(的)包装,,感觉总不是很自然..而且还会导致一个概念多个配置,,webwork2就需要在Spring里配置bean,,再配置自己(的)xwork文件..想象一下吧,,我们们(的)bean直接就是一个controller,,直接可以完成MVC(的)所有任务,,这是多少爽(的)感觉..
Rod Johnson采用IOC容器来实现(的)另一个原因是这会减少好多开发工作量..看一下urlMapping吧,,它们提供(的)property本身就是一个HashMap,,只有配置完成,,我们们(的)bean里(的)数据就自然存在了,,哈哈,,好爽吧..不用象struts那样解析XML,, 再把它们(的)内容一项一项(地)读到HashMap里..
虽然这样(的)配置会有点怪异,,但假如我们们对Spring(的)IOC 容器非常熟悉(的)话,,会发现它们非常(的)亲切,,也非常(的)简单..
最后是一个简单(的)小秘密,,Spring怎么知道某个 bean(的)配置就是urlMapping?另一个bean(的)配置就是viewResolver?其实很简单,,把所有(的)bean全部读到内存里,,然后通过bean(的)名字或类型去找就行了..通过名字去找就是简单(的)getBean技巧,,通过类型去找则使用了 BeanFactoryUtils.beansOfTypeIncludingAncestors(的)静态技巧..
二、Spring 提供了明确(的)Model,, View概念和相应(的)数据结构
在Spring里有一个有趣(的)数据类型叫做 ModelAndView,,它们只是简单(地)把要显示(的)数据和显示(的)结果封装在一个类里..但是它们却提供了明确(的)MVC概念,,尤其是 model概念(的)强化,,使程序(的)逻辑变(得)更清晰了..
记(得)以前在Struts里写程序里(的)时候,,为了显示数据经常自己把东西放到HttpSession或HttpServletRequest里(或set到form里,,虽然不太有用),,这造成了model概念(的)模糊,,而且也导致了struts与JSP页面(的)紧耦合..假如我们们要替换成Veloctiy,,就(得)另外加一个plugin,,因为在velocity里数据是不需要不放到request里(的)..
Webwork2里强调(的)是与Web framework解耦和它们(的)command模式(的)简单性,,因此在它们(的)action里只有简单(的)get或set技巧,,假如返回数据,,也只是简单(地)返回一个String.当然这样(的)实现有它们(的)好处,,但是它们淡化了model和view(的)概念..Rod Johnson认为Webwork2里(的)Action同时包含了Action和Model(的)职责,,这样一个类(的)职责太多,,不是一个很好 (的)设计..当然Jason Carreira不太认同这种观点,,因为Action里(的)model对象完成可以delege给其它们对象..但不管怎样,,这种争论(的)根源在于Webwork2里淡化了model,, view甚至web(的)概念..仁者见仁,,智者见智,,最后(的)结果还是看个人喜欢好吧..
三、 Spring(的)Controller是Singleton(的),,或者是线程不安全(的)
和Struts一样,,Spring(的)Controller是Singleton(的),,这意味着每个request过来,,系统都会用原有(的)instance 去处理,,这样导致了两个结果:我们们不用每次创建Controller,,减少了对象创建和垃圾收集(的)时间;由于只有一个 Controller(的)instance,,当多个线程调用它们(的)时候,,它们里面(的)instance变量不是线程安全(的)..
这也是Webwork2吹嘘(的)(地)方,,它们(的)每个Action都是线程安全(的)..因为每过来一个request,,它们就创建一个 Action对象..由于现代JDK垃圾收集功能(的)效率已经不成问题,,所以这种创建完一个对象就扔掉(的)模式也(得)到了好多人(的)认可..Rod Johnson甚至以此为例证明J2EE提供(的)object pool功能是没多大价值(的)..
但是当人们在吹嘘线程安全怎么怎么重要(的)时候,,我们想请问有多少人在多少情况下需要考虑线程安全?Rod Johnson在分析EJB(的)时候也提出过其它们问题,,并不是没有了EJB(的)线程安全魔法,,世界就会灭亡(的),,大多数情况下,,我们们根本不需要考虑线程安全(的)问题,,也不考虑object pool.因为我们们大多数情况下不需要保持instance状态..
至少我们写了那么多(的)struts Action,,写了那么多(的)Spring Controller,,几乎没有碰到需要在instance变量保持状态(的)问题..当然也许是我们写(的)代码不够多,,Struts(的)设计者 Craig R. McClanahan曾经说当时他们设计struts时有两个条件不成熟:当时没有测试驱动开发(的)概念;当时JVM(的)垃圾收集性能太次..假如现在重新设计(的)话,,他们也会采用每个request生成一个新对象(的)设计技巧,,这样可以解决掉线程安全(的)问题了..
四、 Spring不象Webwork2或tapestry那样去隐藏Servlet相关(的)元素如HttpServletRequest或 HttpServletResponse
这又是一个重要(的)设计决定..在Webwork2里我们们没有 HttpServletRequest或者HttpServletResponse,,只有getter,, setter或ActionContext里数据,,这样(的)结果导致一个干净(的)Action,,一个与Web完全无关(的)Action,,一个可以在任何环境下独立运行(的)bean.那么Webwork2(的)这样一个基于Command模式(的)Action究竟给我们们带来了什么?我们想主要有两点:
1、它们使我们们(的)Action可以非常容易(地)被测试..
2、用户可以在Action里添加业务逻辑,,并被其它们类重用..
然而仔细跟Spring比较一下,,我们们就会发现这两点功能所带来(的)好处其实并不象我们们想象 (的)那么显著..Spring(的)Controller类也可以非常轻松被测试,,看一下spring-mock下面(的)包吧,,它们提供 (的)MockHttpServletRequest,, MockHttpServletResponse还有其它们一些类让测试Controller变(得)异常轻松..再看一下Action里(的)业务逻辑吧,,Jason Carreira曾经说我们们可以尽情(地)在Webwork2(的)Action里加业务逻辑,,因为Action是不依赖于Web(的)..但是有多少人真正往Action里加业务逻辑(的)?大多数人都会业务逻辑delegate给另一个Service类或Manager类..因为我们们很清楚,, 往Action里加业务逻辑会使整个体系(的)分层架构变(得)不清晰,,不管怎样,,Web层就是Web层,,业务层就是业务层,,两者(的)逻辑混在一起总会带来问题(的)..而且往Action里加业务逻辑会使用这个Action类变(得)庞大,,Webwork2(的)Action是每个 request都创建实例(的),,尽管带来(的)性能影响不太大,,但并不表示每次都要把业务逻辑再new出来,,业务逻辑在大多数(的)情况下应该是单例(的)..
不把request和response展现给用户当然还会带来功能上(的)损失,,也许一般(的)场合,,用用 webwork2提供(的)接口已经足够了,,但有时我们们必须要知道request和response才能发挥出更大(的)威力..比如我们以前(的) 一个项目里有一个通过递归动态生成(的)树状结构(的)页面,,在jsp页面上显示递归是痛苦或不可能(的),,因此我们用response直接 write出页面,,这在spring里很easy,,但在webwork里可能比较难了(偶不敢肯定,,偶研究(得)不够深,,也许高手是有办法 (的))..
五、Spring提供了不错但不够充分(的)interceptor机制
回头看一下struts,,它们在架构里甚至没有给我们们提供hook point(的)机会,,我们们没有任何机会加入自己(的)interceptor.我们们只能通过重载 struts(的)RequestProcessor类来进行一点有限(的)扩展..
到了Webwork2,,似乎 interceptor一下子成了整个Framework(的)核心,,除了Action(的)核心部件,,其它们所有(的)东西都是 interceptor.它们(的)超强(的)interceptor功能使们扩展整个架构变(得)非常方便..有人称这种interceptor为 AOP,,Jason Carreira则自豪(地)宣称这个叫做pragamtic AOP.我们不认同这是AOP,,它们只是简单(的)interceptor机制..但不管如何,,它们(的)interceptor确实有强大(的)功能..
Spring也提供了它们(的)interceptor机制,,它们(的)HandlerInterceptor三个 interceptor技巧:peHandle,, postHandle,, afterCompletion.分别对应Controller执行前,,Controller执行后和page render之后..虽然大多数情况下已经够用,,但是从功能上来说显然它们没有Webwork2强大..从AOP(的)角度来看,,它们没有提供 around interceptor,,而只有before与after interceptor.这意味着我们们无法在interceptor前后保持状态,,最简单(的)情况假如我们们要计算一个Contr12下一页