前段时间, Struts 1.x 正式宣布退出舞台,作为一个历史悠久的 MVC 框架,Struts 1.x 影响了不少的开发者,甚至可以说,很多人就是通过 Struts 学习和认识 MVC 的。为了纪念这位“先驱”,同时帮助那些仍在使用 Struts 1.x 框架的开发者更好地过渡到其他 MVC 框架,InfoQ 邀请了几位专家:
- 李锟,知名技术专家,资深架构师,在 REST 等领域颇有建树。出版过《REST 实战》、《Ajax 实战》等多部译作。
- 张逸,一个怀揣梦想的架构师,沉迷于设计之美,游走于.NET 与 Java 之间,但更偏好关注架构与设计本质,著译作包括《软件设计精要与模式》、《WCF 服务编程》等。
- 张龙,热衷于编程,乐于分享,对 Java 轻量级框架有一定研究,工作流领域研究者,资深培训讲师。翻译出版了《Spring 高级程序设计》、《设计原本》等书籍。
大家一起畅谈他们印象中的 Struts 1.x,以及如何选择框架等等问题。
1. Struts 是最早的 MVC 框架之一,影响了很多人,你是否还记得最早接触到它时,给你留下的印象是什么?
李锟:我在 2000 年就开始做 Java Web 开发,使用的技术是 JSP+JavaBean。很快我们就遇到了代码重用难、维护不便的问题。我在网上找到一篇文章,介绍了 Java Web 开发的两种模式 Model 1 和 Model 2。我理解了什么是 Model 2 和 Web MVC 设计模式,然后又知道 Struts 1.x。当时我对这个框架很感兴趣,花了一些时间学习。不过因为工作变化,当时没有用起来。 2003 年,我在一家公司做电子政务类的项目开发,公司创始人开发了一套基于 XMLHttp 和 XML 模板的快速框架。虽然我们开发的应用只支持 IE,但是可以说那个时候我们就已经在做 Ajax 开发了。电子政务类的项目,经常要开发比较复杂的 Tree、Data Grid 等 Web 前端控件。很显然 Struts 1.x 这种瘦客户端的开发框架对此帮不上什么忙,我们需要的是富客户端开发框架,甚至是一个完全依赖 JavaScript 的开发框架。我们当时对 Struts 1.x 的看法是:中看不中用,开发效率低下(相对我们自己的框架),已经过时了。
后来我在工作中陆续使用过 Struts 1.x 和 Struts 2.x。我曾经把一个开源的基于 Struts 1.x 的自助式广告联盟应用移植到 Spring MVC,还基于 Struts 2.x 做过网站开发。Struts 1.x 的主要问题是框架的侵入性太大,不利于代码重用和单元测试。Struts 2.x 确实进步很大,完全基于 POJO,学习曲线低了很多,还支持零配置(不需要 XML 配置,甚至也不需要 Annotation)。尽管如此,Struts 2.x 仍然明显落后于时代。
- Struts 2.x 这一类老牌 Web MVC 开发框架仅能用于开发瘦客户端应用,无法用来开发对于交互体验要求更高的应用。
- Struts 2.x 的 REST 插件对 REST 的支持非常差,连子资源都不支持,只能当作一个玩具。
和我们在 2004 年时的预测相同,目前富客户端+RESTful Web Services 已经大行其道,先是各种基于 JavaScript 的 One Page 开发框架,接下来是移动 Web 应用的爆发。我预测 Struts 2.x 在大约 3 年之后也会走向自己的终点。事实上 Struts 2.x 从 2007 年到现在几乎没有进步,其灵魂人物 Rickard Oberg(创造了 WebWork 和 Struts 2.x)早已经离去。
张逸:从业 15 年来,我主要参与的开发工作除了早期的 Windows Form 应用开发外,主要还是集中在后端。用分层的角度来说,即工作在数据层、领域层以及服务层。虽然早已知道 Struts 的大名,甚至了解到所谓 Struts+Spring+Hibernate 几乎成为了 Java 企业开发的标配,却一直没有机会使用 Struts。我当时工作的项目主要还是在微软的.NET 平台上,经历了从 ASP 到 ASP.NET,再到 ASP.NET MVC 的过程。在使用 ASP 时,我很质疑那种业务代码与表现代码混杂在一起的开发方式;而在最初拥抱 ASP.NET 时,我认为这种 Code-Behind 会是一种良好的职责分离。然而,ASP.NET 在灵活性或扩展性方面带来的约束,使得它越来越不适合富客户端的开发了。于是,才有了借鉴 Ruby On Rails 思想的 ASP.NET MVC 出现。或许我的回答偏题了,但我想借 ASP 的这种发展来看待此次 Struts 1.x 退出历史舞台的事件,那就是任何产品都会步入晚年的衰落期,跟不上技术的发展,必然会被淘汰,没有什么好奇怪的。
张龙:我记得第一次使用 Struts 框架是在 2005 年,那时还在上海上学,在一家公司做兼职项目开发时用到的这个框架。在使用这个框架之前我仅仅对 JSP、Servlet 有过一些经验,对于 MVC 模式也只限于理论上的了解。当时在学校做一些项目的时候用得比较多的还是基于 Model 2 的开发模式,即 JSP+Servlet。我印象特别深的是在开始使用 Struts 框架时完全感觉无从下手,一下子觉得变得太复杂了,一段很简单的表单提交逻辑就涉及到 Struts 的各种标签、FormBean、Action,当然还有内容颇为庞杂的 struts-config.xml 文件。当时一边写心里一边在想,为什么要这么复杂,JSP+Servlet 就可以轻松搞定的事情为什么要写这么多类,还有那个复杂的配置文件,一时半会儿真是无法理解。好在随着项目的不断进行,当逻辑变得越来越复杂,类文件数量变得越来越多时我才逐渐感受到 Struts 框架带给我们的好处:强迫开发人员将不同层次的代码写到不同的文件中,通过配置文件就可以清晰了解整个项目的逻辑运转过程,这个过程我大概花了半个月左右的时间才算是比较透彻地理解清楚了。我觉得很多学习 Struts 框架的人一开始的想法应该与我有很多相似之处。Struts 框架可以说是 Java Web 开发领域的一个里程碑,是最早的 MVC 框架之一,影响了一代 Java Web 开发人员这种说法也不算过。
2. Struts 1.x 即将走完最后的历程,对于那些仍在使用它的系统,你有什么建议?如果要升级,有几个备选方案,例如 Struts 2.x、Spring MVC 等等,你会如何选择?
李锟:我建议选择迁移到 Spring MVC,我在这方面有一些实战经验。Spring MVC 既支持 Struts 1.x 的开发方式(一个 Action 是一个 Singleton 对象,里面有很多方法),也支持 WebWork 那种基于 Command 模式的开发方式(一个 Action 是一个 Prototype 对象,只有一个方法)。从 Struts 1.x 移植到 Spring MVC 的难度不会很大,另外由于 Spring MVC 仍然在持续发展,并且能够较好地支持 REST 开发,在我看来应该选择哪个框架是很明显的。
张逸:奇怪的是,虽然在早期我不曾有机会使用 Struts 1.x,然而我现在正在工作的一个大型项目,因为其漫长的历史,一部分 Web 前端使用的正好是 Struts 1.x。对于这种正在使用它的系统,若要说有何建议,简言之,还是需要在决策时视情况而定。在我们当前项目的一个子系统中,Struts 1.x 是与 Spring MVC 2.0 共存的;而在另一个子系统中,又存在 Struts 2.x 与 Spring MVC 3.x 共存的情况。从架构的一致性来看,这是很不合理的;然而就项目的真实情况,我又认为这种现象未尝不可。迁移的成本往往是昂贵的,尤其对于遗留系统而言,若没有覆盖率极高的验收测试,盲目地为了追求架构一致性进行迁移,反而会引入新的问题。这就需要权衡迁移的成本和迁移得到的好处。在 Java 平台下,可供选择的成熟 Web 框架并不多,Struts 2.x 以及 Spring MVC 相较于 Struts 1.x 而言,主要还是体现在模式上的区别,属于侵入性更小、架构更为简单的框架。相对于升级,我更倾向于保留原有框架,对于新增的功能则可以引入更新的框架。若因为种种原因硬要升级,我更倾向于选择 Spring MVC,一方面它与 Spring 框架的集成度更好,学习曲线低;同时它对于 Struts 1.x 实现方式的固有支持,会使得迁移的成本会降低。最重要的一点是 Spring MVC 目前还保有一定程度的活力,它的版本还在演化中;相对而言,Struts 似乎已经失去活力了。 若抛开这些成熟的 Web 框架不谈,我的建议是不妨试试 Java 平台下的其他框架,例如 jRails,Spring Roo、Apach Wicket 或者 Play。若想继续工作在 Spring 的技术栈下,Spring Roo 会是一个有趣的选择。事实上,你可以认为它是 Spring 所要力推的下一代 Web 框架,如果你不想重蹈 Struts 1.x 的覆辙,可以在决策时冒着风险给予提前尝鲜的机会。Play 框架是基于 Java 和 Scala 开发的 Web 框架,它似乎更偏重于建造可伸缩性的 Web 框架。此外,它的安全模块、持久化支持 (包括对 NoSQL 与 Big Data 的支持)、RESTful 以及 Mobile 的支持,使得它更适合开发当今的 Web 应用程序。
张龙:前不久 Apache 宣布 Struts 将 EOL,其实我个人认为这对于尚在使用 Struts 进行开发的项目来说不算是世界末日,毕竟 Struts 从第一个版本到现在已经经过了很多年的发展,框架代码也已经变得非常成熟,而且由于开源,开发人员完全可以通过源代码了解 Struts 的实现原理,并没有什么秘密可言,而且 Struts 相关的文档与资料也已经非常多了,所以我觉得并不会对现有项目造成太大影响。何况对于那些采用 SAP 进行二次开发的企业来说,由于标准代码就是基于 Struts 框架的,因此采用 Struts 也是不二之选。从这个角度来说,我认为 Struts 的 EOL 只是随着软件开发发展的一种正常现象,毕竟十多年前的设计与架构现在已经无法更好满足软件开发之所需了,也算是软件生命周期的一种必然结果。 如果是新的项目,我想选择 Struts 进行开发的企业应该还是少数,因为现在已经有了更多、更好的选择。比如说 Struts2、Spring MVC 等。我对于这两个框架都有一定的使用经验,也开发过不少项目。我是个技术实用主义者,其实我个人认为无论是 Struts2 还是 Spring MVC 都是顺应新时代的软件开发潮流而产生的,在我个人看来,这两个框架在设计哲学上存在较大的差异,但从实际项目开发的角度来说,无论哪一个框架都是可以胜任的。众所周知,Struts2 来源于 Opensymphony(很遗憾,Opensymphony 已经于一年前关闭了,其下的众多项目也被独立出去单独开发了)的 WebWork,它与 Struts 没有任何关系,只是借助于当时非常流行的 Struts 这个名字而已,实质上还是完全沿用了 WebWork 的设计思想与 XWork 这个微内核、基于命令模式的框架,另外在 ValueStack 和标签库上做了一些增强和改进。而 Spring MVC 则是一个后起之秀,并且在 Spring 的大旗下发展迅猛。从设计角度来说,Struts2 采用了基于 POJO 的思想,摒弃了 Struts 的 FormBean,这样每一个 Action 都是一个有状态的对象,因此需要针对每个请求创建一个新的 Action 实例。另外由于采用了 OGNL,这使得 Struts2 的标签库变得非常灵活和强大,又由于 Struts2 的插件设计思想,我们可以非常轻松地采用已有的插件和开发自己的插件扩展 Struts2 的功能,这使得 Struts2 的可扩展性变得非常强,目前 Struts2 的官网上已经有了几十种常见插件供我们使用,比如 Spring 集成、注解插件、REST 插件、与一些前端框架的集成等,对我们的开发起到了非常大的帮助作用。
Spring MVC 的设计思想与 Struts2 截然不同,它采取的策略有些类似于传统的 Servlet,即每一个 Controller 都是无状态的单例,客户端参数的传递是通过自定义方法参数得到的,而且相对于 Struts2 的设计来说,Spring MVC 的设计更加灵活,比如说我们可以自定义各种各样的方法、可以通过各种方式匹配客户端的请求,而不像 Struts2 那样只能通过 URL 来匹配,但这也带来一个问题,就是开发人员可能每个人使用的方式都不一样,为项目的后期管理和维护带来一定的问题。因此,在我所经历的 Spring MVC 项目中,都会提前定义好各种规范和契约,使得大家遵循共同的访问模式,这样比较有利于项目的统一。
从我个人角度来说,我觉得选择 Struts2 还是 Spring MVC 还是看项目组开发人员的技术掌握程度,或是想学一些新的技术这个方面的考量,毕竟二者都非常成熟,而且都与 Spring 有着良好的集成。此外要提的一点是,Spring MVC 提倡基于注解的配置,Struts2 默认使用 XML(当然也可以通过插件使用基于注解的配置),这个就要看项目组成员的想法了,有些人喜欢配置文件,有些人喜欢注解,我们不能说哪个好哪个不好,只是一种风格选择而已。
3. 经常会有框架或软件结束生命周期,不再进行维护,这对使用它的用户多多少少带来了一些困扰 ,能否聊聊您在项目最初进行选择时的一些经验之谈。
李锟:我的经验是尽量选择一些基于 POJO 设计的开发框架,这样代码重用和单元测试都更容易。另外尽量选择学习曲线低的开发框架,当然熟练掌握一种复杂的框架或者技术可以作为炫耀的资本,但是如果你处在一个团队之中(而不是单打独斗),引入这样的框架很可能会极大增加团队成员的负担,从而极大增加开发和维护的成本。好的开发框架应该尽量做到透明,应用程序运行于其中,程序员主要关注的应该是实现应用自身的业务逻辑,而不是花很多精力与框架本身搏斗。EJB 2.0 问题非常多,所以现在已经没人用了。进入成本和退出成本都很高的开发框架,哪怕再强大神奇,我也不会选择的。KISS 是一个很好的设计原则,也可以作为开发框架的选择原则。
张逸:对于框架的选择,我比较偏重于框架的简单性和无侵入性这两个特点。简单性可以保证我们快速地理解框架的架构,并能够正确地使用它;无侵入性则使得我们可以避免所谓“供应商锁定”的反模式,在需要迁移框架时,可以尽快摆脱原有框架的约束。当然,这种选择总要结合项目需求,根据风险对各种质量属性进行综合权衡,方能做出合理的设计决策。因此,我会将这两个特点看做是重要的衡量指标,但并非绝对。在一定程度上,我们还可以通过更好的架构设计来规避对框架的依赖,例如通过好的分层设计,或者引入防腐层隔离对框架的依赖。以 Struts 1.x 而言,只要我们避免在 Action 中引入业务逻辑,选用新 Web 框架的成本就会更低一些。同时,保证足够的测试覆盖率是必要的,尤其是足够覆盖率的单元测试与集成测试,它常常可以放缓系统衰老的脚步。对于旧系统的维护或重构而言,测试覆盖率是进行改造的良好基石。
张龙:在项目技术选型时,我还是比较激进的。总是希望在新的项目中让大家能多学到一些东西、新的技术或是新的框架等,当然这是在保证项目按时交付的前提下需要考量的因素。但有一点我是比较在意的,那就是所选择的技术是否已经有了一些成功的案例,资料文档是否比较完备(我们不能要求每个人都仔细研读所用开源项目的源代码),而且项目组中需要有人提前研究相应的框架,这样在遇到问题时才能提出相应的解决方案。无论选择什么技术与框架,需要有人提前研究并给大家进行较为详尽的介绍,同时还要与相关的技术框架进行一些横向对比。至于软件结束生命周期,很多时候是我们无法事先预料到的。但我想如果某个软件结束生命周期,那说明会有更多、更好用的软件出现,这也会给我们带来更大的选择,但要是对现有的软件进行升级可不是一件容易的事情,比如将基于 Struts 的项目升级到 Struts2 就是个极其艰巨的任务,这时还是要仔细权衡了,看看是否继续沿用已有的框架,然后在新的项目中采用其他方案,这并没有统一的解决思路,还是要看实际的项目与业务情况。
4. 随着像 Backbone.js 这样的前端 MVC 框架的流行,Struts 这样的服务器端 MVC 的作用似乎有所减弱,您觉得 MVC 逻辑“前移”会是今后的发展趋势么?
李锟:MVC 逻辑前移肯定是今后的发展趋势。其实在 2005 年 Ajax 和 RIA 技术兴起时,Web MVC 这种瘦客户端应用的设计模式就已经过时了。 当然,Web 应用的范围极为广泛。如果考虑到 SEO,还是有很多应用必须要做成瘦客户端形式的,所以 Web MVC 开发框架仍然会存在很多年。但是对于移动应用来说,Web MVC 肯定已经过时了。注意这里我说的是 Web MVC,而不是 MVC。在移动应用中,MVC 作为一种基础设计模式,其实现已经完全前移到客户端,而服务器端则简化为单纯的数据提供者,通过 RESTful Web Services 为客户端提供数据。
张逸:我完全赞同这一点。在我参与的上一个项目中,对于服务端的 Web 层而言,几乎就成为了一个 Controller+JSON+REST 的组合,MVC 中的 M 被 JSON 或资源所替代,V 则干脆消失了,由 Controller 来负责必要的服务端验证,并完成 HTTP 请求的路由功能,其余的前端逻辑都交由我们当初选择的 ExtJS 了。 这种设计是完全合理的。但我仍然要说明一点的是,这种设计由于加大了前端的复杂度,因而需要我们更加关注前端的代码质量。传统开发要求的关注点分离、松耦合与高内聚原则同样适用于这样的前端代码。虽然不一定要提倡前端代码的测试驱动开发,但至少要保证这些代码具有足够的测试覆盖率。例如,我们可以为 Javascript(或者 jQuery) 引入 Jasmine,QUnit 等测试框架。在我们曾经参与的一个项目中,由最初只支持一个品牌,增强到支持多个品牌的需求变化,这其中需要涉及到对大量前端代码的复用。由于之前的设计并未考虑到多品牌的支持,因而需要重构前端代码,以达成复用的目的。如果没有足够的测试覆盖率,以及良好的职责分离,要做到这一点的难度不言而喻。
张龙:Backbone 我在项目中使用过,其实我觉得前端 MVC 框架与 Spring MVC 这样的后端 MVC 框架并不矛盾。他们尝试解决的问题和面向的对象是不同的,一个是如何实现前端的解耦,一个是如何实现后端的解耦,二者肯定会共存,并且都有各自的用武之地。毕竟,前端 MVC 需要大量的 JavaScript、CSS 代码,是不是每个项目都需要做成这样呢?相信每个人心中都有自己的答案。
此外,大家还就未来的开发框架做了些畅想,李锟对开发框架提出了一些要求:
- _ 继续支持 Web MVC 模式 _ 仍然是基础功能,至少不能比 Struts 2.x、Ruby on Rails 这样的传统开发框架做得差。
- 约定先于配置,对于大多数开发场景尽量做到零配置。
- 模块化、插件化、更好的可定制性,开发者可以在核心模块的基础上,根据自己需要添加不同的组件,而不是要么全有、要么全无。
- 低成本。更轻量级、学习曲线更低、性能更好、更容易配置和维护。
- 很好地支持 REST 开发,不仅支持开发 RESTful WebService,还支持开发 RESTful WebApp。
- 集成并提供大量 Web 前端的开发控件,并且能与服务器端组件建立流畅的交互流程。
他认为今后必会出现面向移动 Web 应用(富客户端)开发的一站式框架,从 Web 前端一直到持久层完全打通,把客户端的 MVC 和服务器端的 MVC 结合在一起,其中有 5 个组成部分,客户端有 MVC,服务器端不再有 V,只有 MC。
目前客户端 MVC 开发框架已经非常多了,很多支持 MVC 的 JS 框架,Flash 也有 Pure MVC 等多种选择。但是它们与服务器端的 MVC 开发框架并没有做很深入的整合,这样又出现了当年 Struts、Spring、Hibernate 各自为战的局面。初学者的入门门槛很高。2005 年 Ruby on Rails 横空出世之后,自以为牛 X 的 SSH 立即风光不再。大家恍然大悟,其实 Web 开发应该这样做。复杂不是王道,简化才是王道。
张逸非常认同李锟提到的这个模式:
许多模式事实上都是从 MVC 模式衍生而来,例如 MVP 等。此时,MVC 可以作为核心模式的一个名词。应该为那些变种的模式命名,并给出最佳实践,从而表达特定的含义。
他建议将这种模式命名为 MC2MVC,或者 RC2MVC。有个“2”,就表示从服务端到客户端的意思。至于 RC2MVC,则是为了强调服务端提供的“资源”,而非传统意义上的模型。
笔者也同样非常认同这种模式,但在具体的框架实现上稍有不同:
我相信 MVC 前移这个一定是趋势,而且 JS 的框架现在也越来越强大了,后端服务器上的 MVC 的 V 将渐渐地变为 REST 接口,为前端 MVC 提供数据。但是我并不希望再出现一个类似 Rails 的一站式框架,从前端到后端统统囊括在内。Rails 在刚诞生的时候的确引起了不小的轰动,但是时至今日,它的问题也是非常的多,比如默认开启了太多的功能,还有在非 Web 领域方面的应用问题,Robbin 的那篇《Ruby 社区应该去 Rails 化了》已经写的非常清楚了。我更希望能看到多个框架分别负责前端 MVC,后端的 MC,各自做精做好,然后再有一个粘合剂框架将前后串联起来。大家都能独立发展,可以很方便地替换框架,当然,这也是要求能做到很高的非侵入性。
各位读者,不知您是否也有一些话想对 Struts 1.x 说?您对今后的框架又有何想法呢?不妨一起探讨一下。
『号外』:JavaOne 2013 大会将于 7 月 22–25 日在上海世博中心举行,内容涵盖使用 Java SE 构建现代应用程序、打造针对下一代智能设备的移动和嵌入式 Java 应用程序、编制基于 Java EE 的复杂企业解决方案以及在云环境中安全、无缝地构建和部署业务应用程序等,报名或查看详情请点击。
评论