深入Spring MVC framework之总体分析

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

没有更多推荐了,返回首页

关闭
关闭
关闭