【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

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

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

关注

评论

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

模块一作业

Geek_cb6852

架构实战营

H1 作业1

杨彬

Dubbo源码阅读-泛化调用实现原理

小江

dubbo RPC

80%的人都不会的,15个Linux实用技巧

鞋子特大号

Linux

学生管理系统架构

Fleng

架构实战营

面试官

ES_her0

4月日更

架构实战营 - 模块01作业

架构实战营

架构实战营 模块一作业

fazinter

架构实战营 作业一

架构实战营 模块一课后作业

iProcess

架构实战营

Vue中Echarts基本使用

Chalk

大前端 eCharts 4月日更

#架构实战营 模块一作业

薛定谔的指南针

架构实战营

你才不是只会理论的女同学-seata实践篇

好好学习,天天向上

Java MySQL spring 事务 seata

架构实战营模块1作业

梦寻解语花

架构实战营

编程好习惯之理清数据的可变性

顿晓

4月日更 不可变

【Java试题】从一道题目再看Java继承

程序员架构进阶

Java 面试 28天写作 4月日更

架构实战营模块1作业

CR

模块一

^_^vincent

[架构实战营][0期]模块1学习总结

张民

架构实战营

坚持到底有多难?

小天同学

坚持 自我思考 个人感悟 成长与思考 4月日更

跟着源码一起学:手把手教你用WebSocket打造Web端IM聊天

JackJiang

网络编程 websocket 即时通讯 IM

架构实战营模块一 - 总结

凯迪

架构实战营课程一作业

Saber

架构实战营

架构实战营 模块1

Acker飏

树莓派的组装与开机

IT蜗壳-Tango

IT蜗壳教学 4月日更

服务器如何修复旧加密算法漏洞

运维研习社

4月日更 服务器安全

模块一

yu

架构实战营

架构实战营 - 模块 1- 作业

carl

架构实战营

架构实战营 模块1 课后作业

张伟明

数据结构和算法

奈奈奈奈

数据结构与算法

课程1作业

husx

架构实战营模块1作业

cclouds

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