写点什么

将 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:002292
用户头像

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

关注

评论

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

聚力打造四个“高地”,携手合作伙伴共铸国云!

天翼云开发者社区

云计算 云平台

担心 GitHub?那就试试极狐GitLab 吧

极狐GitLab

git GitHub 开源 DevOps gitlab

兆骑科创高端人才项目引进落地,双创大赛承办,线上直播路演

兆骑科创凤阁

双创大赛承办

spark-streaming状态流之mapWithState

矛始

spark 状态流

@千行百业,一起乘云而上!

天翼云开发者社区

云计算 云平台

Plato Farm有望通过Elephant Swap,进一步向外拓展生态

西柚子

敏捷开发与DevOps的对比

码语者

DevOps 敏捷

大型仿人机器人整机构型研究与应用

优必选科技

机器人

共议公共数据开放,“数牍方案”亮相数字中国建设峰会

Jessica@数牍

隐私计算 数牍科技 公共数据开放

10 万字节Spring Boot +redis详细面试笔记(带完整目录)免费分享

程序员啊叶

Java 编程 程序员 架构 java面试

大咖说·图书分享 | 精益产品开发:原则、方法与实施

大咖说

产品开发 落地方法

带你熟悉云网络的“电话簿”:DNS

华为云开发者联盟

云计算 后端 IP DNS 局域网

一文详解 Redis 中 BigKey、HotKey 的发现与处理

冉然学Java

Java redis 微服务 bigkey HotKey

超越 Nginx!号称下一代 Web 服务器,用起来够优雅

冉然学Java

Java nginx GitHub 服务器 Web、

Qakbot新型感染链:使用Windows7系统侧加载感染设备

郑州埃文科技

dll Windows7 Qakbot

智能家居行业发展,密切关注边缘计算和小程序容器技术

Speedoooo

智能设备 边缘计算 智能家居 小程序容器

Java 将OFD转换为PDF

在下毛毛雨

Java PDF OFD 格式转换

什么是传输层协议TCP/UDP???

C++后台开发

TCP 网络协议 udp 后端开发 C/C++开发

双屏协作效率翻倍 灵耀X双屏Pro引领双屏科技新潮流

科技热闻

实践GoF的23种设计模式:观察者模式

华为云开发者联盟

Web 设计模式 开发 GoF

1对1直播源码——1对1语音聊天源码

开源直播系统源码

直播系统源码 语音聊天系统软件开发 一对一语音聊天软件

使用python玩转文字类视频

技能实验室

签约计划第三季

AOP切入点表达式及五种通知类型解析

王小凡

一文搞懂│XSS攻击、SQL注入、CSRF攻击、DDOS攻击、DNS劫持

网络安全 经验分享 签约计划第三季

我们被一个 kong 的性能 bug 折腾了一个通宵

尔达Erda

程序员 运维 云原生 性能 bug

如何通过ETL调度工具 TASKCTL 使用作业插件类型调用 kettle作业?

敏捷调度TASKCTL

数据仓库 kettle ETL #运维 TASKCTL

Plato Farm有望通过Elephant Swap,进一步向外拓展生态

小哈区块

技术风向标 | 云原生技术架构成熟度模型解读

阿里巴巴云原生

阿里云 云原生 成熟度模型

次轮Okaleido Tiger即将登录Binance NFT,引发社区热议

西柚子

DevSecOps,让速度和安全兼顾

SoFlu软件机器人

BSN IPFS(星际文件系统)专网简介、功能、架构及特性、接入说明

BSN研习社

BSN 分布式存储,

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