写点什么

将 Struts 应用迁移到 Struts 2(一)

  • 2007-04-05
  • 本文字数:5296 字

    阅读完需:约 17 分钟

大多数人都会熟悉 Struts, 无论是从项目实战中获得的经验还是从书中了解到的知识。在这一系列文章里,我们将通过一个由 Struts 迁移到 Struts 2 的简单应用例子向大家展现 Struts 2 的所有特征。

在我们开始介绍这个例子之前,你需要去了解一点 Struts 2 的背景知识。文章的第一部分将介绍 Struts 2 与 Struts 的核心架构的不同点,以助于更好地把所有概念联系起来。第二部分将深入探讨两者在 actions 上的差别、action 相关的框架特征和 action 的配置。在文章最后一部分将会讲述用户界面。我们会讲到其架构、UI 构件、主题和标签,还有如何为我们的应用加上新的外观。

我们并不打算谈及迁移过程的所有细节方面,我们只是从普通的出发点开始介绍 Struts 2 的概念和现在可用的所有新特征。但拥有这些知识后,无论以后迁移到何等规模的应用到 Struts 2 中你都可以易如反掌。

导言 / 历史

Struts 的第一个版本是在 2001 年 5 月份发布的。它的最初设想是通过结合 JSP 和 Servlet,使 Web 应用的视图和业务 / 应用逻辑得以清晰地分离开来。在 Struts 之前,最常见的做法是在 JSP 中加入业务和应用逻辑,或者在 Servlet 中通过println()来生成视图。

自从第一版发布以来,Struts 实际上已成为业界公认的 Web 应用标准。它的炙手可热也为自己带来了改进和变更,所以不但要跟上对 Web 应用框架不断变化的需求,而且要与日渐增多竞争激烈的众多框架的特性相融合。

到最后,产生了几个下一代 Struts 的解决方案。其中两个最受瞩目的方案是 Shale 和 Struts Ti。Shale 是一个基于构件的框架,并在最近成为 Apache 的顶级项目。而 Struts Ti 则是在 Struts 的成功经验基础上继续坚持对前端控制器(Front Controller)和 MVC(model-view-controller)模式进行改进。

WebWork 项目是在 2002 年 3 月发布的,它对 Struts 式框架进行了革命性改进,引进了不少新的思想,概念和功能,但和原 Struts 代码并不兼容。WebWork 是一个成熟的框架,经过了好几次重大的改进与发布。

在 2005 年 12 月,WebWork 与 Struts Ti 宣布合并。与此同时,Struts Ti 改名为 Struts Action Framework 2.0,成为 Struts 真正的继承者。

最后要注意的是,并不是说 Struts 或 WebWork 项目已经停止开发了。由于人们对这两个项目的兴趣仍然很高,而且也有很多开发者仍然愿意使用它们,因此这两个项目还在继续开发中,继续修复 Bug,改进功能和继续添加新功能。

请求运作过程

在我们开始详细探讨如何把应用由 Struts 迁移到 Struts 2 之前,让我们通过体验整个请求流程,看看新架构是如何运作的。

在我们体验了整个请求的生命周期后,你应当注意到很重要的一点——Struts 2 仍是以前端控制器框架为主体的。所有的概念还都是你以前所熟悉的。

这意味着:

  • Actions 仍然是通过 URL 触发的
  • 数据仍然是通过 URL 请求参数和 Form 参数传送到服务端的
  • 所有 Servlet 对象(如 request、response 和 session 等)仍在 Action 可用

以下是请求处理过程的高层概览:

整个请求的处理过程可以分为 6 步:

  1. 由框架产生一个请求并进行处理 - 框架根据请求匹配相应的配置,得到使用哪些拦截器,Action 类和返回结果的信息。
  2. 请求通过一系列的拦截器 - 拦截器和拦截器组可以按照不同级别进行组合配置来处理请求。它们为请求提供各种预处理和切面处理的应用功能。这和 Struts 的使用 Jakarta Commons Chain 构件的 RequestProcessor 类很相似。
  3. 调用 Action - 产生一个新的 Action 对象实例,并提供请求所调用的处理逻辑的方法。我们在第二部文章中将对这步骤进行进一步讨论。Struts 2 可以在配置 Action 时为请求分配其指定的方法。
  4. 调用相应的 Result - 通过匹配处理 Action 方法之后的返回值,获取相应 Result 类,生成并调用它的实例。处理 Result 可能产生的结果之一就是对 UI 模板(但并非只有一个)进行渲染,来产生 HTML。如果是这种情况的话,模板中的 Struts 2 tags 可以直接从 Action 中获取要被要被渲染的值。
  5. 请求再次经过一系列拦截器处理后返回 - Request 以和进入时相反的方向通过拦截器组,当然,你可以在这个过程中进行回收整理或者额外的处理工作。
  6. 响应被返回给用户 - 最后一步是将控制权交还给 Servlet 引擎。最常见的结果是把渲染后的 HTML 返回给用户,但返回的也可能是指定的 HTTP 头或者进行 HTTP 重定向。

你应该已经注意到 Struts 2 和 Struts 的差别了。最明显的就是 Struts 2 是一个 pull-MVC 架构。这是什么意思呢?从开发者角度看,就是说需要显示给用户的数据可以直接从 Action 中获取,而不像 Struts 那样必须把相应的 Bean 存到 Page、Request 或者 Session 中才能获取。

配置框架

首先最重要的是,通过配置 web.xml 文件让框架能在 Servlet 容器里运行。

下面这个就是大家都熟悉的 Struts 在 web.xml 里的配置方法:

<servlet></servlet><br></br><servlet-name></servlet-name>action<br></br><servlet-class></servlet-class>org.apache.struts.action.ActionServlet<br></br><init-param></init-param><br></br><param-name></param-name>config<br></br><param-value></param-value>/WEB-INF/struts-config.xml<p><load-on-startup></load-on-startup>2</p><br></br><servlet-name></servlet-name>action<br></br><url-pattern></url-pattern>*.do<br></br><servlet-mapping></servlet-mapping>在 Struts 2 中,配置有少许改变,最明显的是分发器(dispatcher)已由 Servlet 转为 Servlet Filter, 其配置和 Servlet 一样简单,如下:

<filter></filter><br></br><filter-name></filter-name>webwork<br></br><filter-class></filter-class><br></br> org.apache.struts.action2.dispatcher.FilterDispatcher<br></br><filter-name></filter-name>webwork<br></br><url-pattern></url-pattern>/*<br></br>和 Servlet 配置一样,Filter 配置中定义了 Filter 的名称(作为引用)和类名。Filter Mapping 通过 URI 和名称匹配来调用相应的 Filter。默认情况下,扩展名为“.action”,这是在 default.properties 文件(在 Struts 2 JAR 文件里)的“struts.action.extension”属性定义的。

工具箱:“default.properties”是默认配置选项定义文件。你可以通过在 classpath 中包含一个叫“struts.properties”的文件,设置不同的属性值,来覆盖默认配置的值,实现自己的配置。

对于 Struts 来说, Servlet 配置提供了一个用于定义文件名的 init-param tag 来配置 Struts,而 Struts 2 没有这样的配置参数,取而代之的是在 classpath 下的默认配置文件“struts.xml”。

工具箱 / 提示:因为 Struts Actions(扩展名“.do”)和 Struts 2 Actions(扩展名“.action”)两者的扩展名命名空间不一样,所以 Struts 和 Struts 2 可以在同一个 Web 应用系统中无碍地共存。所以这就为迁移提供了很好的条件,加入适当的配置,新功能的开发都用 Struts 2。保持原有的遗留功能,如果时间和资源允许的情况下再逐步迁移。另一种方法是,只把 Struts 2 的扩展名改为“.do”,这样就可使得以前的 JSP 页面可重用。

解剖 Actions

在上面介绍的请求运作流程中,我们从高层次上谈及了一些 Struts 和 Struts 2 的不同点。现在我们将较深入地探讨这两个框架中 Action 结构的具体差别。

让我们来回顾一下 Struts 的 Action 的主要结构。Struts Action 的主要形式如下:

public class MyAction extends Action {<br></br> public ActionForward execute(ActionMapping mapping,<br></br> ActionForm form,<br></br> HttpServletRequest request,<br></br> HttpServletResponse response)<br></br> throws Exception {<br></br> // do the work<br></br> return (mapping.findForward("success"));<br></br> }<br></br>}当你实现一个 Struts Action 时, 需要注意以下问题:

  1. 所有的 Action 都继承于 Action 基类。
  2. 所有的 Action 都必须是线程安全的,因为只产生一个 Action 实例。
  3. 因为所有的 Action 都必须是线程安全的,所有在 Action 处理过程中所需要的对象都必须以方法参数的形式传入。
  4. 处理 Action 所调用的方法必须命名为“execute”(在 Struts 中的 DispatchAction 类可以调用同一个 Action 的其它方法,但实际上在框架中的入口点仍然是“execute”方法)。
  5. ActionForward 结果是通过 ActionMapping 类中的方法来产生的,通常的做法是通过调用“findForward”方法。

相比较之下,Struts 2 的 Action 提供了更简单的实现方式。下面就是个例子:

public class MyAction {<br></br> public String execute() throws Exception {<br></br> // do the work<br></br> return "success";<br></br> }<br></br>}首先你会注意到的是,Struts 2 中的 Action 不再继承于任何类或需要实现任何接口。实际上,它还远不只这些。按照惯例,只有“execute”方法能调用 Action, 但在 Struts 2 中并非必要,任何声明为 public String methodName() 方法都能通过配置来调用 Action。

另外,你会注意到返回的对象不是 ActionForward,而是 String。如果你不喜欢以字符串的形式出现在你的代码中,有个 Helper 接口 Action 可以以常量方式提供常见结果,如“success”、“none”、“error”、“input”和“login”。

最后,和 Struts 最大的革命性的不同是,处理 Action 过程中调用的方法(“execute”方法)是不带参数的。那你如何获取你所需要的对象呢?答案是使用“反转控制(Inversion of Control)”,也叫“依赖注入(Dependency Injection)”的模式(想更多地了解这方面信息请看 Martin Fowler 的文章 http://www.martinfowler.com/articles/injection.html )。Spring 框架使得这个模式流行起来,然而 Struts 2 的前身(WebWork)也在同时应用上了这个模式。

为了更好地了解反转控制,让我们来看看一个例子,如何在 Action 处理过程中可以访问到当前请求 HttpServerRequest 对象。

在我们的例子中,我们使用的依赖注入机制是接口注入。就如其名称一样,接口注入需要的是已经被实现了的接口。这个接口包含了相应属性的 setter,为 Action 提供值。例子中我们使用了 ServletRequestAware 接口,如下:

public interface ServletRequestAware {<br></br> public void setServletRequest(HttpServletRequest request);<br></br>}当我们继承这个接口后,我们原本简单的 Action 看起来有点复杂了,但是这时我们可以获取 HttpServerRequest 对象来使用了。

public class MyAction implements ServletRequestAware {<br></br> private HttpServletRequest request;<br></br> public void setServletRequest(HttpServletRequest request) {<br></br> this.request = request;<br></br> }<br></br> public String execute() throws Exception {<br></br> // do the work using the request<br></br> return Action.SUCCESS;<br></br> }<br></br>}看起来现在这些属性是类级别的,并不是线程安全的,会出现问题。其实在 Struts 2 里并没有问题,因为每个请求过来的时候都会产生一个新的 Action 对象实例,它并没有和其他请求共享一个对象,所以不需要考虑线程安全问题。

现在我们还有最后一步,就是为这个 Action 关联上ServletConfigInterceptor拦截器。这个拦截器提供了一系列功能去获取 HttpServletRequest,并可以把它注入到实现了ServletRequestAware接口 Action 中。这时你并不需要担心如何配置这些,我们将在下一篇文章中有具体讲述。最重要的是让我们明白到是拦截器和接口共同合作下为 Action 提供了反转控制功能的。

这个设计的好处是能让 Action 和框架完全解耦。Action 仅仅是一个与框架无关的简单 POJO。这给单元测试带来极大的好处,Struts 2 Action 的单元测试远比 Struts Action 使用 StrutsTestCase 或 MockStrutsTestCase 的单元测试简单。

总结 / 综述

到现在为止,你应该对 Struts2 的基础有所了解了——包括高层的框架概念和基础的请求流程。你也应该能自己动手在 Servlet 容器里配置 Struts 2,并理解 Struts 和 Struts 2 两者之间在 Action 方面的差别了。

在下篇文章中,我们将会介绍一个详细的迁移的例子,同时我们也在这章学到的知识基础上,演示如何由 Struts 向 Struts 2 的 Action 迁移。在最后讲述如何在应用中使用 JSTL、JSP 和 Struts2。我们会进一步探讨 Struts 和 Struts 2 在 Action 上的区别,Struts 2 的配置和其他框架元素,和谈到更多的 Action 相关的框架特征。

关于作者

Ian Roughley 是一位技术演讲人、作家及独立咨询顾问,住在马萨诸塞州的波士顿。他具有十多年提供架构设计、开发、过程改进以及指导等方面服务的经验,客户范围小至创业公司,大到财富 500 强前 10 名的公司。他专注于具有实效性且以结果为目标的方法,是开源及以敏捷开发为基础的过程和质量改进的支持者。

关于译者

陈俊 SpringSide 开源项目的主力成员,中科院软件工程硕士,就职于 Accenture。长期从事 J2EE 企业级应用开发, 略有小成。热衷于软件体系结构,设计模式,软件过程改进及敏捷开发研究,以成为出色的架构师为目标。爱尝试不同的开源技术,一直投身 SpringSide 开发,为国内开源出一分绵力。

2007-04-05 20:002319
用户头像

发布了 27 篇内容, 共 10.1 次阅读, 收获喜欢 15 次。

关注

评论

发布
暂无评论
发现更多内容

年轻程序员不讲武德,做表竟然拖拉拽

雯雯写代码

程序员

Python最会变魔术的魔术方法,我觉得是它!

Python猫

学习笔记4

Qx

学习笔记-week12

张荣召

英特尔唐炯:36.4% PC同比增长,预示了2021是个好年

E科讯

双十二好物推荐:「mPaaS 安全加固」带你看看别人家的应用

蚂蚁集团移动开发平台 mPaaS

安全 mPaaS 应用

架构师训练营第 1 期 第 12 周作业

李循律

极客大学架构师训练营

以太公约系统开发详情丨以太公约源码案例

系统开发咨询1357O98O718

以太公约系统开发介绍

架构探索:事务处理二

而立斋

API研发效能提升实战

Geek_40a463

研发效能 API研发

GaussDB(DWS)磁盘维护:vacuum full执行慢怎么办?

华为云开发者联盟

数据库 数据 DWS

排查指南 | 关于 mPaaS-iOS 小程序打不开问题的解决方案

蚂蚁集团移动开发平台 mPaaS

小程序 mPaaS

第五周作业第1题

走走,停停……

无可限量的数字经济

CECBC

数字经济

mongodb 源码实现系列 - mongodb详细表级操作及详细时延统计实现原理(快速定位表级时延抖动)

杨亚洲(专注MongoDB及高性能中间件)

数据库 mongodb 性能调优 源码刨析 分布式数据库mongodb

第八周-总结

jizhi7

Java中CAS原理分析(volatile和synchronized浅析)

叫练

volatile 多线程 synchronized CAS JUC

Eclipse Vert.x 4发布

dinstone

Java Reactive Vert.x

shell脚本的使用该熟练起来了,你说呢?(篇四)

良知犹存

shell脚本编写

架构探索:事务处理三

而立斋

阿里P8大佬带你全面了解—MySQL锁:03.InnoDB行锁

比伯

Java MySQL 编程 架构 程序人生

架构探索:事务处理总结

而立斋

你心目中高级程序员的印象是什么样子的?

Java架构师迁哥

12.2分布式文件系统

张荣召

话题讨论 | 作为程序员你的业余爱好是什么呢?

小天同学

话题讨论 业余爱好

12.1大数据技术发展史

张荣召

区块链技术在旅游业中的应用探索

CECBC

旅游

Java并发编程:任务执行器Executor接口

码农架构

Java并发

第八周课后练习

jizhi7

DolphinDB与Aliyun HybridDB for PostgreSQL在金融数据集上的比较

DolphinDB

postgresql 阿里云 时序数据库 DolphinDB 数据库开发

架构之书:传道与《设计模式》

lidaobing

架构 设计模式

将Struts应用迁移到Struts 2(一)_Java_Ian Roughley_InfoQ精选文章