Skip to content

1. 概述

该文档的PDF版本可在此处获取 |HERE|。

1.1. 目标

本文旨在介绍一种名为STRUTS的开发方法。 Jakarta Struts 是 Apache 软件基金会(www.apache.org)的一个项目,旨在提供一个符合所谓 MVC 架构(模型-视图-控制器)的 Java Web 应用程序标准开发框架。

1.2. MVC 模型

MVC 模型旨在分离展示层、处理层和数据访问层。遵循该模型的 Web 应用程序将采用以下架构:

此类架构被称为三层架构:

  • 用户界面是 V(视图)
  • 应用逻辑是 C(控制器)
  • 数据源是 M(模型)

用户界面通常是网页浏览器,但也可能是通过网络向Web服务发送请求HTTP并格式化其返回结果的独立应用程序。应用逻辑由处理用户请求的脚本组成。 数据源通常是数据库,但也可能是简单的平面文件、目录 LDAP、远程 Web 服务等。开发人员应确保这三个实体之间保持高度独立,以便其中一个发生变化时,另外两个无需或只需少量调整。

当尝试将此模型应用于 Servlet 和 JSP 页面时,将形成以下架构:

在[Logique Applicative]模块中,我们可以区分出

  • 作为应用程序入口点的Servlet(也称为控制器)
  • [Classes métier]模块,其中包含应用程序逻辑所需的Java类。
  • [Classes d'accès aux données] 模块,其中包含获取 Servlet 所需数据的 Java 类,通常是持久化数据(BD、文件、WEB 服务等)
  • JSP 页面模块,构成应用程序的视图。

1.3. 基于Servlet和页面的MVC开发方法

我们已定义了一套符合前述 MVC 模型的 Java Web 应用程序开发流程。在此重申该流程。

  1. 首先,我们将定义应用程序的所有视图。这些视图即呈现给用户的网页。因此,在设计视图时,我们将站在用户的角度出发。视图主要分为三类:
    • 用于收集用户信息的输入表单。该表单通常配有一个按钮,用于将输入的信息发送至服务器。
    • 响应页面,仅用于向用户提供信息。该页面通常包含一个链接,允许用户通过其他页面继续操作应用程序。
    • 混合页面:Servlet向客户端发送了一个包含其生成的信息的页面。该页面将用于客户端向Servlet提供其他信息。
  1. 每个视图都会生成一个 JSP 页面。对于每个页面:
    • 将绘制页面的外观
    • 确定其动态部分:
      • 需由 Servlet 作为参数提供给视图 JSP 的用户信息
      • 需提交给 Servlet 进行处理的输入数据。这些数据应属于表单 HTML 的一部分。
  1. 我们可以将每个视图的输入/输出进行示意图化
    • 输入是指 Servlet 需通过请求 (request) 或会话 (session) 提供给页面 JSP 的数据。
    • 输出是页面 JSP 需提供给 Servlet 的数据。它们属于表单 HTML 的一部分,Servlet 将通过 request.getparameter(...) 类型的操作获取这些数据。
  1. 我们将编写每个视图的 Java/JSP 代码。该代码通常采用以下形式:
<%@ page ... %>    // 最常用的类导入
<%!
     // 页面实例变量 JSP(=全局变量)
     // 仅当页面 JSP 包含共享变量的方法时才需要(很少见)     
    ...    
%>
<%
     // 获取由Servlet发送的数据
     // 无论是请求(request)还是会话(session)中的数据
    ...
%>

<html>
...
         // 此处将致力于简化 Java 代码
</html>
  1. 接下来可以进行初步测试。下面介绍的部署方法适用于 Tomcat 服务器:
    • 应用上下文必须在 Tomcat 的 server.xml 文件中创建。我们可以先测试该上下文。设该上下文为 C,与其关联的文件夹为 DC。 我们将构建一个静态文件 test.html,并将其放置在 DC 文件夹中。 启动 Tomcat 后,通过浏览器访问 URL http://localhost:8080/DC/test.html
    • 每个 JSP 页面均可进行测试。如果某个 JSP 页面名为 formulaire.jsp, 则需通过浏览器访问 URL http://localhost:8080/DC/formulaire.jsp。页面 JSP 需要从调用它的 Servlet 获取参数值。 由于此处是直接调用,因此它不会收到预期的参数。为了仍能进行测试,我们将在页面 JSP 中使用常量自行初始化预期的参数。这些初步测试可验证页面 JSP 的语法是否正确。
  1. 接下来编写 Servlet 的代码。该 Servlet 包含两个截然不同的方法:
    • init 方法用于:
      • 从应用程序的 web.xml 文件中获取配置参数
      • 根据需要创建后续将要使用的业务类实例
      • 管理可能出现的初始化错误列表,该列表将反馈给应用程序的未来用户。此错误处理甚至可延伸至向应用程序管理员发送电子邮件,以通知其系统故障
    • 调用方法 doGet 或 doPost,具体取决于 Servlet 从客户端接收参数的方式。如果 Servlet 处理多个表单,建议每个表单都发送一条可唯一标识其身份的信息。 这可以通过表单中的隐藏字段实现,例如 <input type="hidden" name="action" value="...">。Servlet 可以先读取该参数的值,然后将请求的处理委托给负责处理此类请求的内部私有方法。
    • 应尽可能避免在Servlet中放置业务代码。这不是Servlet的设计初衷。Servlet更像是一个团队负责人(控制器),它接收来自客户(Web客户端)的请求,并将其分派给最合适的执行者(业务类)来处理。 编写 Servlet 时,需确定待实现业务类接口(构造函数、方法)。此步骤仅适用于需新建业务类的情况。若业务类已存在,则 Servlet 应适配现有接口。
    • Servlet的代码将被编译。
  1. 编写 Servlet 所需业务类的骨架。 例如,如果Servlet使用类型为proxyArticles的对象,且该类必须包含一个名为getCodes的方法,该方法返回一个字符串列表(ArrayList),那么我们最初只需编写如下代码:
public ArrayList getCodes(){
    String[] codes= {"code1","code2","code3"};
    ArrayList aCodes=new ArrayList();
    for(int i=0;i<codes.length;i++){
        aCodes.add(codes[i]);
    }
        return aCodes;
}
  1. 接下来可以进行Servlet的测试。
    • 必须创建应用程序的配置文件 web.xml。该文件应包含 Servlet 的 init 方法所需的所有信息(<init-param>)。此外,需通过 URL 设置访问主 Servlet 的映射(<servlet-mapping>)。
    • 所有必要的类(Servlet、业务类)都放置在 WEB-INF/classes 目录下。
    • 所有必要的类库(.jar)都放置在 WEB-INF/lib 目录下这些类库可能包含业务类、JDBC 驱动程序等。
    • 视图文件(JSP)放置在应用程序根目录或独立文件夹中。其他资源(HTML、图片、音频、视频等)也按此方式处理
    • 完成上述步骤后,对应用程序进行测试并修正初期错误。本阶段结束时,应用程序架构即可投入运行。鉴于 Tomcat 本身不提供调试工具,此测试阶段可能较为棘手。为此,需将 Tomcat 集成到开发工具中(如 JBuilder Developer、Sun One Studio 等)。 可借助 System.out.println("....") 指令,该指令会在 Tomcat 窗口中输出信息。首先需验证 init 方法是否正确获取了来自 web.xml 文件的所有数据。 为此,可以将这些数据的值写入 Tomcat 窗口。同样地,需验证 doGet doPost 方法是否正确获取了应用程序中各个 HTML 表单的参数。

编写Servlet所需的业务类。这通常是经典的Java类开发,大多与任何Web应用程序无关。首先,将在该环境之外对其进行测试,例如使用控制台应用程序。当业务类编写完成后,可以将其集成到Web应用程序的部署架构中,并测试其在该架构中的正确集成情况。 每个业务类都将按照此流程进行处理。

1.4. STRUTS开发方法

STRUTS方法论的制定者旨在为基于Java开发的Web应用程序定义一种符合MVC架构的标准化开发方法。STRUTS项目包含两个方面:

  • 开发方法。我们将看到,它与上述针对 JSP Servlet 和页面的描述非常接近
  • 支持该开发方法的工具。这些工具是Apache基金会网站上提供的Java类库(www.apache.org)。

1.4.1. 开发方法

STRUTS 采用的架构如下:

  • 控制器是应用程序的核心。所有客户端请求均通过它处理。这是一个由 STRUTS 提供的通用 Servlet。 在某些情况下,可能需要对其进行派生。对于简单的情况,则无需如此。该通用Servlet通常从一个名为struts-config.xml的文件中获取所需信息。
  • 如果客户端请求包含表单参数,这些参数会被放入一个 Bean 对象中。如果一个类遵循稍后将介绍的构造规则,则被称为 Bean 类。随着时间推移创建的这些 Bean 对象会被存储在会话或客户端请求中。这一点是可以配置的。如果它们已被创建,则无需重新创建。
  • 在配置文件 struts-config.html 中,对于每个需要通过程序处理的 URL(因此不对应于可直接请求的 JSP 视图),会关联以下信息:
    • 负责处理该请求的 Action 类名称。同样,实例化的 Action 对象可保存在会话或请求中。
    • 如果请求的 URL 带有参数(例如向控制器提交表单的情况),则会指定负责存储表单信息的 Bean 的名称。
  • 凭借配置文件提供的这些信息,当控制器接收到客户端发来的 URL 请求时,便能确定是否需要创建 Bean 以及具体创建哪个 Bean。一旦实例化,该 Bean 即可验证其存储的表单数据是否有效。 控制器会自动调用该 Bean 中的 validate 方法。Bean 由开发人员构建,因此开发人员需在 validate 方法中编写验证表单数据有效性的代码。若数据被判定为无效,控制器将不再继续处理,而是将控制权移交给配置文件中指定的视图。至此,交互流程结束。 需要注意的是,开发人员可以要求不验证表单的有效性。他在文件 struts-config.html 中也这样做了。在这种情况下,控制器不会调用 Bean 的 validate 方法。
  • 如果 Bean 数据正确,或者未进行验证,或者不存在 Bean,控制器将控制权移交给与 URL 关联的 Action 类型对象。 它通过调用该对象的execute方法来实现,并将可能已构建的Bean引用传递给该方法。此时开发者需要完成相应的工作:可能需要调用业务类或数据访问类。处理完成后,Action对象将控制器应发送给客户端的视图名称返回给控制器。
  • 控制器发送该响应。与客户端的交互至此结束。

STRUTS开发方法论初具雏形:

  • 视图的定义。需区分表单视图与其他视图。
    • 每个表单视图都会在文件 struts-config.xml 中生成一个定义。其中定义了以下信息:
      • 将包含表单数据的 Bean 类名称,以及是否需要对数据进行验证的标记。如果需要验证且数据被判定为无效,则需在此处指定应作为响应发送给客户端的视图。
      • 负责处理表单的Action类名称。
      • 所有在请求处理完成后可能作为响应发送给客户端的视图名称。Action类将根据处理结果从中选择一个。
    • 每个视图对应一个 JSP 页面。我们将看到,在视图中(特别是表单视图),有时会使用 Struts 专有的标签库。
  • 编写与表单视图对应的 JavaBean 类
  • 编写负责处理表单的Action
  • 编写业务类或数据访问类(如有)

1.4.2. STRUTS开发工具

STRUTS项目是Apache软件基金会(Apache Software Foundation)的项目之一。该基金会多个项目统称为Jakarta,可在http://jakarta.apache.org上获取:

Image

建议阅读此页面。其中许多项目对 Java 开发者颇具价值。若点击上文的 Struts 链接,将跳转至该项目的首页:

Image

同样,建议您阅读首页内容。要下载Struts的Java库,请点击上方的“Binaries”链接:

Image

Windows用户请使用链接1.1.zip,Unix用户请使用1.1.tar.gz(2003年11月版)。解压文件1.1.zip后,将得到以下目录结构:

Image

在此目录结构中,包含 STRUTS 开发所需的 Java 类库。这些类库存储在 .jar 或 .war 文件中,其作用类似于 .zip 文件,可使用相同的工具进行解压。大部分所需的类库位于上述 lib 文件夹中:

Image

除 .jar 类库外,还包含 .dtd(文档类型定义)文件,其中定义了 XML 文件的有效性规则。XML 文件的内容中可引用此类 DTD 文件。 分析 XML 文件内容的程序(称为解析器)将使用被引用的 DTD 文件中的有效性规则,来判断 XML 文件的语法是否正确。 例如,文件 struts-config_1_1.dtd 规定了 Struts 1.1 版本配置文件 struts-config.xml 的构建规则。

现在让我们看看应将 Struts 目录树中的各个元素放置在何处,以便在 Tomcat 服务器上部署一个 Struts 应用程序。

1.5. 部署 Struts 应用程序

Struts 应用程序与其他 Web 应用程序并无二致。因此,它遵循其运行所在容器的部署规则。 在此,我们将一个名为 strutspersonne 的应用程序部署在 Tomcat 4.x 版本服务器上。附录中提供了 Tomcat 5.x 版本的部署方法。本文遵循 Tomcat 部署规则 4.x:

  1. 我们在 Tomcat 的配置文件 server.xml 中定义 strutspersonne 上下文:
                <Context path="/strutspersonne" docBase="e:/data/serge/web/struts/personne" />

完成上述操作后,可能需要重启 Tomcat 以便其应用新的上下文。可通过访问 URL http://localhost:8080/strutspersonne 来验证上下文是否有效:

Image

如果未出现错误页面,则说明上下文配置正确。

  1. 我们在与 strutspersonne 上下文关联的物理文件夹中,创建子文件夹 WEB-INF
  2. 在应用程序的 WEB-INF 文件夹中,我们定义应用程序的配置文件 web.xml

Image

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
      <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

</web-app>
  • 该应用程序的控制器(Servlet)类是 Struts 的预定义类,名为 ActionServlet。它位于文件 struts.jar 中。 为了让 Tomcat 能找到该类,我们将 struts.jar 放置在 <tomcat>\common\lib 文件夹中,这是 Tomcat 在搜索类时会遍历的文件夹之一。 实际上,我们将把 <struts>\lib 文件夹中找到的所有 .jar 文件都放置在此处,其中 <struts> 是 Struts 目录树的根目录。

Image

  • 还将把位于 <struts>\contrib\struts-el\lib 目录下的 struts-el.jar jstl.jar 文件放入该目录:

Image

  • 在此,我们可以访问 Web 服务器。但并非总是如此。 如果将 Web/Java 应用程序部署到非自管型的 Web 容器中,建议让应用程序自带所需的所有库。此时,这些库应放置在需要创建的 WEB-INF/lib 文件夹中。
  • 我们提到控制器需要一些信息,这些信息通常位于与 web.xml 位于同一文件夹中的 struts-config.xml 文件中。实际上,该文件的名称是可以配置的。上文中的 config 参数用于确定该名称。
  • <servlet-mapping>标签表明,所有以.do后缀结尾的URL文件都将调用该控制器。这种映射是Struts所必需的。 随后,这些 URL 请求将由控制器进行过滤,控制器仅接受在其配置文件 struts-config.xml 中声明的 URL 请求

目前,我们的 web.xml 文件已足够。

  1. 我们将向 strutspersonne 应用程序请求 URL /main.do。 根据之前的 web.xml 文件,该 URL 将被传递给 org.apache.struts.action Servlet。 ActionServlet 类将被实例化,并调用其 init 方法。该方法会尝试读取由 config 参数定义的配置文件,因此该文件必须存在。我们创建以下 struts-config.xml 文件:
<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    <action-mappings>
      <action
          path="/main"
          parameter="/main.html"
          type="org.apache.struts.actions.ForwardAction"
      />
    </action-mappings>
</struts-config>

请注意,DTD 文件中的 struts-config.xmlweb.xml 文件中的并不相同,这表明它们的结构不同。 对于控制器需要处理的每个 URL,我们都需要定义一个 <action> 标签。该标签用于告知控制器在被请求此 URL 时应执行什么操作。在此,我们指定了以下内容:

  1. path="/main":定义由<action>标签配置的URL的名称。后缀.do是隐含的。
  2. type="org.apache.struts.actions.ForwardAction":定义处理该请求的Action类名称。此处使用Struts中预定义的Action类。该类本身不执行任何操作,仅将客户端的请求转发至parameter属性中指定的URL。
  3. parameter="/main.html":请求应转发到的 URL 的名称。此处是一个静态的 HTML 文件。

简而言之,当用户请求 URL /main.do 时,将获得 URL /main.html

  1. main.html文件内容如下:
<html>
    <head>
      <title>Application strutspersonne</title>
  </head>
  <body>
      Application strutspersonne active ....
  </body>
</html>

该文件位于 strutspersonne/vues 应用程序文件夹中:

Image

可以通过 URL 直接调用它http://localhost:8080/strutspersonne/main.html

Image

在此情况下,应用程序的 Struts 控制器并未介入,因为它仅在请求 *.do 类型的 URL 时才会介入。而此处请求的是 URL /vues/main.html

  1. 之前生成的文件 struts-config.xml 应放置在与文件 web.xml 相同的文件夹 WEB-INF 中:

Image

  1. 现在,我们将通过请求 URL /main.do 来验证 strutspersonne 应用程序控制器是否正常运行,如有必要,请先重启 Tomcat。

Image

在此,由于我们请求了 *.do 类型的 URL,因此 Struts 控制器已介入处理。我们确实获得了预期的页面(main.html)。 至此,我们已掌握应用程序运行的基础要素:strutspersonne上下文、配置文件web.xml和struts-config.xml,以及Struts库。

如果我们请求的是 /toto.do 类型的 URL 会发生什么?根据 strutspersonne 应用程序的 web.xml 文件,此时将调用 Struts 控制器来处理它。 该控制器随后会查阅其配置文件 struts-config.html,却找不到针对 URL /toto 的任何配置。那么它会怎么做呢?让我们试一试:

Image

我们得到了一页错误信息,这似乎很正常。现在我们可以开始编写应用程序了。