Struts 2 快速指南



基本 MVC 架构

Model View Controller,简称MVC,是一种用于开发 Web 应用程序的软件设计模式。MVC 模式由以下三个部分组成:

  • 模型 (Model) - 最底层,负责维护数据。

  • 视图 (View) - 负责向用户显示全部或部分数据。

  • 控制器 (Controller) - 控制模型和视图之间交互的软件代码。

MVC 流行的原因在于它将应用程序逻辑与用户界面层隔离开,并支持关注点分离。控制器接收所有应用程序请求,然后与模型协作准备视图所需的数据。视图然后使用控制器准备的数据生成最终的可呈现响应。MVC 抽象可以以图形方式表示如下。

Struts MVC

模型

模型负责管理应用程序的数据。它响应来自视图的请求,也响应来自控制器的更新指令。

视图

这意味着以特定格式呈现数据,由控制器的决定触发。它们是基于脚本的模板系统,例如 JSP、ASP、PHP,并且非常易于与 AJAX 技术集成。

控制器

控制器负责响应用户输入并对数据模型对象执行交互操作。控制器接收输入,验证输入,然后执行修改数据模型状态的业务操作。

Struts 2 是一个基于 MVC 的框架。在接下来的章节中,我们将了解如何在 Struts 2 中使用 MVC 方法。

Struts 2 - 概述

Struts 2 是一个流行且成熟的 Web 应用程序框架,基于 MVC 设计模式。Struts 2 不仅仅是 Struts 1 的新版本,而是对 Struts 架构的完全重写。

Webwork 框架最初以 Struts 框架为基础,其目标是提供一个基于 Struts 的增强和改进的框架,使开发人员更容易进行 Web 开发。

一段时间后,Webwork 框架和 Struts 社区携手创建了著名的 Struts 2 框架。

Struts 2 框架特性

以下是一些可能促使您考虑使用 Struts 2 的强大特性:

  • POJO 表单和 POJO Action - Struts 2 取消了 Struts 框架中不可或缺的 ActionForm。使用 Struts 2,您可以使用任何 POJO 来接收表单输入。同样,您现在也可以将任何 POJO 视为 Action 类。

  • 标签支持 - Struts 2 改进了表单标签和新标签,允许开发人员编写更少的代码。

  • AJAX 支持 - Struts 2 认识到 Web 2.0 技术的兴起,并通过创建 AJAX 标签将 AJAX 支持集成到产品中,此功能与标准 Struts 2 标签非常相似。

  • 易于集成 - 与 Spring、Tiles 和 SiteMesh 等其他框架的集成现在更容易,Struts 2 提供了各种集成选项。

  • 模板支持 - 支持使用模板生成视图。

  • 插件支持 - 可以使用插件增强和扩展核心 Struts 2 行为。Struts 2 提供了许多插件。

  • 性能分析 - Struts 2 提供集成的性能分析功能来调试和分析应用程序。此外,Struts 还借助内置调试工具提供集成的调试功能。

  • 易于修改标签 - 可以使用 Freemarker 模板调整 Struts 2 中的标签标记。这不需要 JSP 或 Java 知识。基本的 HTML、XML 和 CSS 知识就足以修改标签。

  • 更少的配置 - Struts 2 通过使用各种设置的默认值来减少配置。除非偏离 Struts 2 设置的默认设置,否则您无需进行配置。

  • 视图技术 - Struts 2 很好地支持多种视图选项(JSP、Freemarker、Velocity 和 XSLT)

以上列出了Struts 2 的十大主要特性,使其成为一个企业级框架。

Struts 2 的缺点

尽管 Struts 2 具有许多强大的特性,但当前版本 Struts 2 也有一些需要改进的局限性。以下是一些主要方面:

  • 较高的学习曲线 - 要将 MVC 与 Struts 一起使用,您必须熟悉标准 JSP、Servlet API 和一个庞大而复杂的框架。

  • 文档不足 - 与标准的 servlet 和 JSP API 相比,Struts 的在线资源较少,许多第一次使用 Struts 的用户发现 Apache 的在线文档令人困惑且组织混乱。

  • 不够透明 - 与普通的基于 Java 的 Web 应用程序相比,Struts 应用程序在幕后发生了更多的事情,这使得难以理解该框架。

最后,一个好的框架应该提供通用的行为,以便许多不同类型的应用程序都可以使用它。

Struts 2 是最好的 Web 框架之一,被广泛用于开发富互联网应用程序 (RIA)。

Struts 2 - 环境搭建

我们的首要任务是运行一个最小的 Struts 2 应用程序。本章将指导您如何准备开发环境以开始使用 Struts 2。

我假设您已经在计算机上安装了 JDK(5+)、Tomcat 和 Eclipse。如果您尚未安装这些组件,请按照以下快速入门步骤操作:

步骤 1 - 安装 Java 开发工具包 (JDK)

您可以从 Oracle 的 Java 网站下载最新版本的 SDK:Java SE 下载。您将在下载的文件中找到安装 JDK 的说明,请按照说明安装和配置设置。最后,设置 PATH 和 JAVA_HOME 环境变量以指向包含 java 和 javac 的目录,通常分别为 java_install_dir/bin 和 java_install_dir。

如果您运行的是 Windows 并将 SDK 安装在 C:\jdk1.5.0_20,则应在 C:\autoexec.bat 文件中输入以下行。

set PATH = C:\jdk1.5.0_20\bin;%PATH%
set JAVA_HOME = C:\jdk1.5.0_20

或者,在 Windows NT/2000/XP 上:

  • 您可以右键单击“我的电脑”,选择“属性”,然后选择“高级”,然后选择“环境变量”。然后,您可以更新 PATH 值并按“确定”按钮。

  • 在 Unix(Solaris、Linux 等)上,如果 SDK 安装在 /usr/local/jdk1.5.0_20 并且您使用的是 C shell,则应将以下内容放入 .cshrc 文件中。

在 Unix(Solaris、Linux 等)上,如果 SDK 安装在 /usr/local/jdk1.5.0_20 并且您使用的是 C shell,则应将以下内容放入 .cshrc 文件中。

setenv PATH /usr/local/jdk1.5.0_20/bin:$PATH
setenv JAVA_HOME /usr/local/jdk1.5.0_20

或者,如果您使用集成开发环境 (IDE),例如 Borland JBuilder、Eclipse、IntelliJ IDEA 或 Sun ONE Studio,请编译并运行一个简单的程序以确认 IDE 知道您安装 Java 的位置,否则请根据 IDE 的文档进行正确的设置。

步骤 2 - 安装 Apache Tomcat

您可以从 https://tomcat.net.cn/ 下载最新版本的 Tomcat。下载安装后,将二进制分发版解压缩到方便的位置。

例如,在 Windows 上为 C:\apache-tomcat-6.0.33,或在 Linux/Unix 上为 /usr/local/apachetomcat-6.0.33,并创建指向这些位置的 CATALINA_HOME 环境变量。

您可以通过在 Windows 计算机上执行以下命令来启动 Tomcat,或者您可以简单地双击 startup.bat。

%CATALINA_HOME%\bin\startup.bat
 
or
 
C:\apache-tomcat-6.0.33\bin\startup.bat

可以通过在 Unix(Solaris、Linux 等)计算机上执行以下命令来启动 Tomcat:

$CATALINA_HOME/bin/startup.sh
 
or
 
/usr/local/apache-tomcat-6.0.33/bin/startup.sh

成功启动后,Tomcat 包含的默认 Web 应用程序可以通过访问https://127.0.0.1:8080/ 来使用。如果一切正常,则应显示以下结果:

Tomcat Home page

有关配置和运行 Tomcat 的更多信息,请参阅此处包含的文档以及 Tomcat 网站:https://tomcat.net.cn/

可以通过在 Windows 计算机上执行以下命令来停止 Tomcat:

%CATALINA_HOME%\bin\shutdown

or

C:\apache-tomcat-5.5.29\bin\shutdown

可以通过在 Unix(Solaris、Linux 等)计算机上执行以下命令来停止 Tomcat:

$CATALINA_HOME/bin/shutdown.sh

or

/usr/local/apache-tomcat-5.5.29/bin/shutdown.sh

步骤 3 - 安装 Eclipse (IDE)

本教程中的所有示例均使用 Eclipse IDE 编写。我建议您在计算机上安装最新版本的 Eclipse。

要安装 Eclipse,请从 https://www.eclipse.org/downloads/ 下载最新的 Eclipse 二进制文件。下载安装后,将二进制分发版解压缩到方便的位置。

例如,在 Windows 上为 C:\eclipse,或在 Linux/Unix 上为 /usr/local/eclipse,最后适当地设置 PATH 变量。可以通过在 Windows 计算机上执行以下命令来启动 Eclipse,或者您可以简单地双击 eclipse.exe。

%C:\eclipse\eclipse.exe

可以通过在 Unix(Solaris、Linux 等)计算机上执行以下命令来启动 Eclipse:

$/usr/local/eclipse/eclipse

成功启动后,如果一切正常,则应显示以下结果:

Eclipse Home page

步骤 4 - 安装 Struts 2 库

现在,如果一切正常,则可以继续安装 Struts 2 框架。以下是您在计算机上下载和安装 Struts 2 的简单步骤。

  • 选择您是要在 Windows 上还是 Unix 上安装 Struts 2,然后继续下一步下载 Windows 的 .zip 文件和 Unix 的 .tar 文件。

  • https://struts.apache.org/download.cgi 下载最新版本的 Struts 2 二进制文件。

  • 撰写本教程时,我下载了struts-2.0.14-all.zip,解压缩下载的文件后,您将在 C:\struts-2.2.3 中获得如下所示的目录结构。

Sturts Directories

第二步是将 zip 文件解压缩到任何位置,我在我的 Windows 7 计算机的c:\文件夹中下载并解压缩了struts-2.2.3-all.zip,以便我将所有 jar 文件都放在C:\struts-2.2.3\lib中。确保您正确设置了 CLASSPATH 变量,否则运行应用程序时会遇到问题。

Struts 2 - 架构

从高层次来看,Struts2是一个拉取式MVC(或MVC2)框架。Struts2中的模型-视图-控制器模式由以下五个核心组件实现:

  • Action(动作)
  • 拦截器(Interceptors)
  • 值栈/OGNL
  • 结果/结果类型(Results / Result types)
  • 视图技术(View technologies)

Struts 2与传统的MVC框架略有不同,其中Action扮演模型的角色,而不是控制器的角色,尽管两者之间存在一些重叠。

Struts 2 Architecture

上图描述了Model(模型)、View(视图)和Controller(控制器)在Struts2高层次架构中的位置。控制器由Struts2分发器servlet过滤器和拦截器实现,模型由Action实现,视图是结果类型和结果的组合。值栈和OGNL提供了一个公共线程,连接并实现了其他组件之间的集成。

除了上述组件之外,还有许多与配置相关的信息。包括Web应用程序的配置,以及Action、拦截器、结果等的配置。

这是Struts 2 MVC模式的架构概述。我们将在后续章节中更详细地介绍每个组件。

请求生命周期(Request Life Cycle)

基于上图,您可以理解在Struts 2中用户请求生命周期的工作流程如下:

  • 用户向服务器发送请求,请求某些资源(例如页面)。

  • 过滤器分发器查看请求,然后确定相应的Action。

  • 应用已配置的拦截器功能,例如验证、文件上传等。

  • 根据请求的操作执行选定的Action。

  • 如果需要,再次应用已配置的拦截器进行任何后处理。

  • 最后,视图准备结果并将结果返回给用户。

Struts 2 - Hello World 示例

正如您已经从Struts 2架构中学到的那样,当您在Struts 2 Web应用程序中单击超链接或提交HTML表单时,输入由控制器收集,然后发送到称为Action的Java类。Action执行后,结果会选择一个资源来呈现响应。该资源通常是JSP,但也可以是PDF文件、Excel电子表格或Java Applet窗口。

假设您已经构建了开发环境。现在,让我们继续构建第一个Hello World Struts2项目。该项目的目的是构建一个Web应用程序,收集用户的姓名,并显示“Hello World”以及用户名。

对于任何Struts 2项目,我们都需要创建以下四个组件:

序号 组件和描述
1

Action

创建一个Action类,它将包含完整的业务逻辑并控制用户、模型和视图之间的交互。

2

拦截器(Interceptors)

根据需要创建拦截器,或使用现有的拦截器。这是控制器的一部分。

3

视图(View)

创建JSP来与用户交互,获取输入并呈现最终消息。

4

配置文件(Configuration Files)

创建配置文件来关联Action、视图和控制器。这些文件是struts.xml、web.xml、struts.properties。

我将使用Eclipse IDE,因此所有必需的组件都将在动态Web项目下创建。现在让我们开始创建动态Web项目。

创建动态Web项目(Create a Dynamic Web Project)

启动Eclipse,然后选择文件 > 新建 > 动态Web项目,输入项目名称为HelloWorldStruts2,并按照以下屏幕设置其余选项:

Hello World Sturts1

在接下来的屏幕中选择所有默认选项,最后选中生成Web.xml部署描述符选项。这将在Eclipse中为您创建一个动态Web项目。现在选择窗口 > 显示视图 > 项目资源管理器,您将看到如下所示的项目窗口:

Hello World Sturts2

现在将以下文件从Struts 2 lib文件夹C:\struts-2.2.3\lib复制到我们项目的WEB-INF\lib文件夹。为此,您可以简单地将所有以下文件拖放到WEB-INF\lib文件夹中。

  • commons-fileupload-x.y.z.jar
  • commons-io-x.y.z.jar
  • commons-lang-x.y.jar
  • commons-logging-x.y.z.jar
  • commons-logging-api-x.y.jar
  • freemarker-x.y.z.jar
  • javassist-.xy.z.GA
  • ognl-x.y.z.jar
  • struts2-core-x.y.z.jar
  • xwork-core.x.y.z.jar

创建Action类(Create Action Class)

Action类是Struts 2应用程序的关键,我们在Action类中实现大部分业务逻辑。因此,让我们在Java资源 > src下创建一个名为HelloWorldAction.java的java文件,包名为com.tutorialspoint.struts2,内容如下所示。

当用户单击URL时,Action类会响应用户的操作。执行Action类的一个或多个方法,并返回一个String结果。根据结果的值,将呈现特定的JSP页面。

package com.tutorialspoint.struts2;

public class HelloWorldAction {
   private String name;

   public String execute() throws Exception {
      return "success";
   }
   
   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

这是一个非常简单的类,只有一个名为“name”的属性。我们为“name”属性提供了标准的getter和setter方法,以及一个返回字符串“success”的execute方法。

Struts 2框架将创建HelloWorldAction类的对象,并在响应用户操作时调用execute方法。您可以将业务逻辑放在此方法中,该方法最终返回String常量。换句话说,对于每个URL,您都必须实现一个Action类,您可以直接使用该类名作为您的Action名称,也可以使用struts.xml文件将其映射到其他名称,如下所示。

创建视图(Create a View)

我们需要一个JSP来呈现最终消息,当预定义的Action发生时,Struts 2框架将调用此页面,此映射将在struts.xml文件中定义。因此,让我们在Eclipse项目中的WebContent文件夹中创建下面的jsp文件HelloWorld.jsp。为此,右键单击项目资源管理器中的WebContent文件夹,然后选择新建 > JSP文件

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      Hello World, <s:property value = "name"/>
   </body>
</html>

taglib指令告诉Servlet容器,此页面将使用Struts 2标签,并且这些标签将以s开头。

s:property标签显示Action类属性“name”的值,该值由HelloWorldAction类的getName()方法返回。

创建主页(Create Main Page)

我们还需要在WebContent文件夹中创建index.jsp。此文件将作为初始操作URL,用户可以单击它来告诉Struts 2框架调用HelloWorldAction类的定义方法并呈现HelloWorld.jsp视图。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
   <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      <h1>Hello World From Struts2</h1>
      <form action = "hello">
         <label for = "name">Please enter your name</label><br/>
         <input type = "text" name = "name"/>
         <input type = "submit" value = "Say Hello"/>
      </form>
   </body>
</html>

上面视图文件中定义的hello Action将使用struts.xml文件映射到HelloWorldAction类及其execute方法。当用户单击“提交”按钮时,它将导致Struts 2框架运行HelloWorldAction类中定义的execute方法,并根据方法的返回值选择和呈现相应的视图作为响应。

配置文件(Configuration Files)

我们需要一个映射来将URL、HelloWorldAction类(模型)和HelloWorld.jsp(视图)绑定在一起。映射告诉Struts 2框架哪个类将响应用户的操作(URL),将执行该类的哪个方法,以及根据该方法返回的String结果呈现哪个视图。

因此,让我们创建一个名为struts.xml的文件。由于Struts 2要求struts.xml位于classes文件夹中。因此,在WebContent/WEB-INF/classes文件夹下创建struts.xml文件。Eclipse默认情况下不会创建“classes”文件夹,因此您需要自己创建。为此,右键单击项目资源管理器中的WEB-INF文件夹,然后选择新建 > 文件夹。您的struts.xml应该如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   
   <package name = "helloworld" extends = "struts-default">
     <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
      </action>
   </package>
</struts>

关于上述配置文件,需要理解一些内容。在这里,我们将常量struts.devMode设置为true,因为我们在开发环境中工作,我们需要查看一些有用的日志消息。然后,我们定义一个名为helloworld的包。

当您想要将Action分组在一起时,创建包非常有用。在我们的示例中,我们将Action命名为“hello”,它对应于URL/hello.action,并由HelloWorldAction.class支持。当调用URL/hello.action时,将运行HelloWorldAction.classexecute方法。如果execute方法的结果返回“success”,那么我们将用户带到HelloWorld.jsp

下一步是创建一个web.xml文件,它是任何对Struts 2的请求的入口点。Struts2应用程序的入口点将是在部署描述符(web.xml)中定义的过滤器。因此,我们将在web.xml中定义org.apache.struts2.dispatcher.FilterDispatcher类的条目。web.xml文件需要在WebContent下的WEB-INF文件夹下创建。当您创建项目时,Eclipse已经为您创建了一个web.xml文件的框架。因此,让我们修改它如下:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
   
   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
   
   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

我们指定index.jsp作为我们的欢迎文件。然后我们配置Struts2过滤器在所有URL上运行(即,任何与模式/*匹配的URL)。

启用详细日志(To Enable Detailed Log)

通过在WEB-INF/classes文件夹下创建logging.properties文件,您可以在使用Struts 2时启用完整的日志记录功能。在您的属性文件中保留以下两行:

org.apache.catalina.core.ContainerBase.[Catalina].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].handlers = \
   java.util.logging.ConsoleHandler

默认的logging.properties指定了一个ConsoleHandler用于将日志记录路由到stdout,还有一个FileHandler。可以使用SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST或ALL设置处理程序的日志级别阈值。

就是这样。我们准备使用Struts 2框架运行我们的Hello World应用程序。

应用程序执行步骤(Procedure for Executing the Application)

右键单击项目名称,然后单击导出 > WAR文件以创建WAR文件。

然后将此WAR部署到Tomcat的webapps目录。

最后,启动Tomcat服务器并尝试访问URLhttps://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将显示以下屏幕:

Hello World Struts4

输入值“Struts2”并提交页面。您应该看到下一页

Hello World Struts5

请注意,您可以在struts.xml文件中将index定义为Action,在这种情况下,您可以将索引页面称为https://127.0.0.1:8080/HelloWorldStruts2/index.action。请查看如何在下面将index定义为Action:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">

      <action name = "index">
         <result >/index.jsp</result>
      </action>

      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
      </action>

   </package>
</struts>

Struts 2 - 配置文件(Struts 2 - Configuration Files)

本章将引导您完成Struts 2应用程序所需的 基本配置。在这里,我们将了解如何在一些重要的配置文件(如web.xml、struts.xml、strutsconfig.xmlstruts.properties)的帮助下进行配置。

说实话,您只需使用web.xmlstruts.xml配置文件就可以开始工作(正如您在我们上一章中已经看到的那样,我们的示例使用了这两个文件)。但是,为了您的知识,我们还将解释其他文件。

web.xml文件

web.xml配置文件是一个J2EE配置文件,它决定了servlet容器如何处理HTTP请求的元素。它严格来说不是Struts2配置文件,但是它是Struts2工作必需的一个配置文件。

如前所述,此文件为任何Web应用程序提供了一个入口点。Struts2应用程序的入口点将是在部署描述符(web.xml)中定义的过滤器。因此,我们将在web.xml中定义一个FilterDispatcher类的入口。web.xml文件需要创建在WebContent/WEB-INF文件夹下。

如果您在没有模板或生成它的工具(例如Eclipse或Maven2)的帮助下开始,那么这是您需要配置的第一个配置文件。

以下是我们在上一个示例中使用的web.xml文件的内容。

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
   
   <display-name>Struts 2</display-name>
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
   
   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

</web-app>

请注意,我们将Struts 2过滤器映射到/*,而不是/*.action,这意味着所有URL都将由Struts过滤器解析。我们将在讲解注解章节时对此进行介绍。

struts.xml文件

struts.xml文件包含您在开发操作时将修改的配置信息。此文件可用于覆盖应用程序的默认设置,例如struts.devMode = false以及在属性文件中定义的其他设置。此文件可以在WEB-INF/classes文件夹下创建。

让我们看一下我们在上一章中解释的“Hello World”示例中创建的struts.xml文件。

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
     
      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
      </action>
      
      <-- more actions can be listed here -->

   </package>
   <-- more packages can be listed here -->

</struts>

首先要注意的是DOCTYPE。所有Struts配置文件都需要具有正确的doctype,如我们的示例所示。是根标签元素,我们使用标签在其中声明不同的包。这里的允许配置的分离和模块化。当您拥有大型项目并且项目被分成不同的模块时,这非常有用。

例如,如果您的项目有三个领域——business_application、customer_application和staff_application,那么您可以创建三个包并将相关的操作存储在相应的包中。

package标签具有以下属性:

序号 属性 & 描述
1

name (必需)

包的唯一标识符

2

extends

此包扩展自哪个包?默认情况下,我们使用struts-default作为基本包。

3

abstract

如果标记为true,则该包不可用于最终用户使用。

4

namespace

操作的唯一命名空间

constant标签以及name和value属性应用于覆盖在default.properties中定义的任何以下属性,就像我们刚刚设置struts.devMode属性一样。设置struts.devMode属性允许我们在日志文件中查看更多调试消息。

我们为要访问的每个URL定义action标签,并定义一个包含execute()方法的类,每当我们访问相应的URL时,都会访问该方法。

结果确定操作执行后返回到浏览器的内容。从操作返回的字符串应该是结果的名称。结果如上所述按操作配置,或者作为“全局”结果配置,可用于包中的每个操作。结果具有可选的nametype属性。默认名称值为“success”。

struts.xml文件可能会随着时间的推移而变得很大,因此通过包将其分解是一种模块化的方法,但Struts提供了另一种模块化struts.xml文件的方法。您可以将文件拆分成多个xml文件,并以如下方式导入它们。

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <include file="my-struts1.xml"/>
   <include file="my-struts2.xml"/>
</struts>

我们尚未介绍的另一个配置文件是struts-default.xml。此文件包含Struts的标准配置设置,对于您99.99%的项目,您无需更改这些设置。因此,我们不会对此文件进行详细介绍。如果您有兴趣,请查看struts2-core-2.2.3.jar文件中提供的default.properties文件。

struts-config.xml文件

struts-config.xml配置文件是Web客户端中视图和模型组件之间的链接,但对于您99.99%的项目,您无需更改这些设置。

配置文件基本上包含以下主要元素:

序号 拦截器 & 描述
1

struts-config

这是配置文件的根节点。

2

form-beans

在这里,您可以将ActionForm子类映射到名称。您在strutsconfig.xml文件的其余部分甚至在JSP页面上使用此名称作为ActionForm的别名。

3

global forwards

此部分将Web应用程序上的页面映射到名称。您可以使用此名称来引用实际页面。这避免了在网页上硬编码URL。

4

action-mappings

在这里,您可以声明表单处理程序,它们也被称为操作映射。

5

controller

此部分配置Struts内部结构,在实际情况下很少使用。

6

plug-in

此部分告诉Struts在哪里可以找到您的属性文件,这些文件包含提示和错误消息

以下是struts-config.xml文件示例:

<?xml version = "1.0" Encoding = "ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
   "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">

<struts-config>

   <!-- ========== Form Bean Definitions ============ -->
   <form-beans>
      <form-bean name = "login" type = "test.struts.LoginForm" />
   </form-beans>

   <!-- ========== Global Forward Definitions ========= -->
   <global-forwards>
   </global-forwards>

   <!-- ========== Action Mapping Definitions ======== -->
   <action-mappings>
      <action
         path = "/login"
         type = "test.struts.LoginAction" >

         <forward name = "valid" path = "/jsp/MainMenu.jsp" />
         <forward name = "invalid" path = "/jsp/LoginView.jsp" />
      </action>
   </action-mappings>

   <!-- ========== Controller Definitions ======== -->
   <controller contentType = "text/html;charset = UTF-8"
      debug = "3" maxFileSize = "1.618M" locale = "true" nocache = "true"/>

</struts-config>

有关struts-config.xml文件的更多详细信息,请查看您的Struts文档。

struts.properties文件

此配置文件提供了一种更改框架默认行为的机制。实际上,struts.properties配置文件中包含的所有属性也可以使用init-paramweb.xml中配置,也可以使用struts.xml配置文件中的constant标签配置。但是,如果您希望将事物分开并使其更特定于Struts,则可以在WEB-INF/classes文件夹下创建此文件。

在此文件中配置的值将覆盖在default.properties中配置的默认值,default.properties包含在struts2-core-x.y.z.jar发行版中。您可以考虑使用struts.properties文件更改一些属性:

### When set to true, Struts will act much more friendly for developers
struts.devMode = true

### Enables reloading of internationalization files
struts.i18n.reload = true

### Enables reloading of XML configuration files
struts.configuration.xml.reload = true

### Sets the port that the server is run on
struts.url.http.port = 8080

这里任何以hash (#)开头的行都将被视为注释,并将被Struts 2忽略。

Struts 2 - Action

Action是Struts2框架的核心,就像任何MVC(模型-视图-控制器)框架一样。每个URL都映射到一个特定的action,该action提供处理请求所需的处理逻辑。

但是action还在其他两个重要方面发挥作用。首先,action在将数据从请求传输到视图(无论是JSP还是其他类型的结果)的过程中发挥着重要作用。其次,action必须协助框架确定哪个结果应该呈现将在对请求的响应中返回的视图。

创建Action

Struts2中action的唯一要求是必须有一个不带参数的方法返回String或Result对象,并且必须是POJO。如果未指定不带参数的方法,则默认行为是使用execute()方法。

您可以选择扩展ActionSupport类,该类实现了包括Action接口在内的六个接口。Action接口如下:

public interface Action {
   public static final String SUCCESS = "success";
   public static final String NONE = "none";
   public static final String ERROR = "error";
   public static final String INPUT = "input";
   public static final String LOGIN = "login";
   public String execute() throws Exception;
}

让我们看一下“Hello World”示例中的action方法:

package com.tutorialspoint.struts2;

public class HelloWorldAction {
   private String name;

   public String execute() throws Exception {
      return "success";
   }
   
   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

为了说明action方法控制视图这一点,让我们对execute方法进行以下更改,并扩展ActionSupport类,如下所示:

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class HelloWorldAction extends ActionSupport {
   private String name;

   public String execute() throws Exception {
      if ("SECRET".equals(name)) {
         return SUCCESS;
      } else {
         return ERROR;  
      }
   }
   
   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

在这个例子中,我们在execute方法中有一些逻辑来查看name属性。如果属性等于字符串"SECRET",我们返回SUCCESS作为结果,否则我们返回ERROR作为结果。因为我们扩展了ActionSupport,所以我们可以使用字符串常量SUCCESS和ERROR。现在,让我们修改我们的struts.xml文件,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction"
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
         <result name = "error">/AccessDenied.jsp</result>
      </action>
   </package>
</struts>

创建视图(Create a View)

让我们在eclipse项目的WebContent文件夹中创建下面的jsp文件HelloWorld.jsp。为此,右键单击项目资源管理器中的WebContent文件夹,然后选择新建>JSP文件。如果返回结果为SUCCESS(即Action接口中定义的字符串常量“success”),则将调用此文件:

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      Hello World, <s:property value = "name"/>
   </body>
</html>

以下是在action结果为ERROR(等于字符串常量“error”)的情况下框架将调用的文件。以下是AccessDenied.jsp的内容:

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>  
   <head>
      <title>Access Denied</title>
   </head>
   
   <body>
      You are not authorized to view this page.
   </body>
</html>

我们还需要在WebContent文件夹中创建index.jsp。此文件将作为初始action URL,用户可以单击它来告诉Struts 2框架调用HelloWorldAction类的execute方法并呈现HelloWorld.jsp视图。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
   <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>  
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      <h1>Hello World From Struts2</h1>
      <form action = "hello">
         <label for = "name">Please enter your name</label><br/>
         <input type = "text" name = "name"/>
         <input type = "submit" value = "Say Hello"/>
      </form>
   </body>
</html>

就是这样,web.xml文件不需要更改,因此让我们使用我们在“示例”章节中创建的同一个web.xml文件。现在,我们可以使用Struts 2框架运行我们的Hello World应用程序了。

执行应用程序

右键单击项目名称,然后单击导出>WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将显示以下屏幕:

Hello World Struts4

让我们输入一个单词“SECRET”,您应该会看到以下页面:

helloworldstruts51

现在输入除“SECRET”以外的任何单词,您应该会看到以下页面:

helloworldstruts6

创建多个Action

您将经常定义多个action来处理不同的请求并为用户提供不同的URL,因此您将定义如下所示的不同类:

package com.tutorialspoint.struts2;
import com.opensymphony.xwork2.ActionSupport;

class MyAction extends ActionSupport {
   public static String GOOD = SUCCESS;
   public static String BAD = ERROR;
}

public class HelloWorld extends ActionSupport {
   ...
   public String execute() {
      if ("SECRET".equals(name)) return MyAction.GOOD;
      return MyAction.BAD;
   }
   ...
}

public class SomeOtherClass extends ActionSupport {
   ...
   public String execute() {
      return MyAction.GOOD;
   }
   ...
}

您将在struts.xml文件中配置这些action,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   
   <package name = "helloworld" extends = "struts-default">
      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorld" 
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
         <result name = "error">/AccessDenied.jsp</result>
      </action>
      
      <action name = "something" 
         class = "com.tutorialspoint.struts2.SomeOtherClass" 
         method = "execute">
         <result name = "success">/Something.jsp</result>
         <result name = "error">/AccessDenied.jsp</result>
      </action>
   </package>
</struts>

正如您在上面的假设示例中看到的,action结果SUCCESSERROR是重复的。

为了解决这个问题,建议您创建一个包含结果结果的类。

Struts 2 - 拦截器

拦截器在概念上与servlet过滤器或JDK的Proxy类相同。拦截器允许在action和框架之外分别实现横切功能。您可以使用拦截器实现以下功能:

  • 在调用action之前提供预处理逻辑。

  • 在调用action之后提供后处理逻辑。

  • 捕获异常,以便可以执行备用处理。

Struts2框架中提供的许多功能都是使用拦截器实现的;

示例包括异常处理、文件上传、生命周期回调等。事实上,由于Struts2将其大部分功能都强调在拦截器上,因此每个action不太可能分配7个或8个拦截器。

Struts2框架拦截器

Struts 2框架提供了一系列开箱即用的拦截器,这些拦截器已预先配置并可以使用。下面列出了一些重要的拦截器:

序号 拦截器 & 描述
1

alias

允许参数在请求之间具有不同的名称别名。

2

checkbox

通过为未选中复选框添加false参数值来辅助管理复选框。

3

conversionError

将将字符串转换为参数类型时的错误信息放入action的字段错误中。

4

createSession

如果尚不存在HTTP会话,则自动创建一个HTTP会话。

5

debugging

为开发人员提供几个不同的调试屏幕。

6

execAndWait

在action在后台执行时,将用户发送到中间等待页面。

7

exception

将从action抛出的异常映射到结果,允许通过重定向进行自动异常处理。

8

fileUpload

方便文件上传。

9

i18n

在用户会话期间跟踪选定的区域设置。

10

logger

通过输出正在执行的action的名称来提供简单的日志记录。

11

params

在action上设置请求参数。

12

prepare

这通常用于执行预处理工作,例如设置数据库连接。

13

profile

允许为action记录简单的概要分析信息。

14

scope

在会话或应用程序范围内存储和检索action的状态。

15

ServletConfig

为action提供对各种基于servlet的信息的访问。

16

timer

以操作执行时间长短的形式提供简单的性能分析信息。

17

令牌

检查操作的有效令牌,以防止重复表单提交。

18

验证

为操作提供验证支持。

有关上述拦截器的完整详细信息,请参阅 Struts 2 文档。但是,我将向您展示如何在 Struts 应用程序中一般使用拦截器。

如何使用拦截器?

让我们看看如何将现有的拦截器用于我们的“Hello World”程序。我们将使用**计时器**拦截器,其目的是测量执行操作方法所花费的时间。同时,我使用**参数**拦截器,其目的是将请求参数发送到操作。您可以尝试在不使用此拦截器的情况下运行您的示例,您会发现**name**属性未设置,因为参数无法到达操作。

我们将保留 HelloWorldAction.java、web.xml、HelloWorld.jsp 和 index.jsp 文件,因为它们已在**示例**章节中创建,但让我们修改**struts.xml**文件以添加拦截器,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <constant name = "struts.devMode" value = "true" />
   
   <package name = "helloworld" extends = "struts-default">
      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction"
         method = "execute">
         <interceptor-ref name = "params"/>
         <interceptor-ref name = "timer" />
         <result name = "success">/HelloWorld.jsp</result>
      </action>
   </package>
</struts>

右键单击项目名称,然后单击**导出 > WAR 文件**以创建 WAR 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL **https://127.0.0.1:8080/HelloWorldStruts2/index.jsp**。这将生成以下屏幕:

Hello World Struts 4

现在在给定的文本框中输入任何单词,然后单击“Say Hello”按钮以执行已定义的操作。现在,如果您检查生成的日志,您会发现以下文本:

INFO: Server startup in 3539 ms
27/08/2011 8:40:53 PM 
com.opensymphony.xwork2.util.logging.commons.CommonsLogger info
INFO: Executed action [//hello!execute] took 109 ms.

底部的行是由于**计时器**拦截器生成的,它表明操作总共花费了 109 毫秒。

创建自定义拦截器

在您的应用程序中使用自定义拦截器是提供横切应用程序功能的一种优雅方式。创建自定义拦截器很容易;需要扩展的接口是以下**Interceptor**接口:

public interface Interceptor extends Serializable {
   void destroy();
   void init();
   String intercept(ActionInvocation invocation)
   throws Exception;
}

顾名思义,init() 方法提供了一种初始化拦截器的方法,而 destroy() 方法提供了一种拦截器清理工具。与操作不同,拦截器在请求之间被重用,并且需要是线程安全的,特别是 intercept() 方法。

**ActionInvocation**对象提供对运行时环境的访问。它允许访问操作本身以及调用操作和确定操作是否已被调用的方法。

如果您不需要初始化或清理代码,则可以扩展**AbstractInterceptor**类。这提供了 init() 和 destroy() 方法的默认无操作实现。

创建拦截器类

让我们在**Java 资源 > src**文件夹中创建以下 MyInterceptor.java 文件:

package com.tutorialspoint.struts2;

import java.util.*;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class MyInterceptor extends AbstractInterceptor {

   public String intercept(ActionInvocation invocation)throws Exception {

      /* let us do some pre-processing */
      String output = "Pre-Processing"; 
      System.out.println(output);

      /* let us call action or next interceptor */
      String result = invocation.invoke();

      /* let us do some post-processing */
      output = "Post-Processing"; 
      System.out.println(output);

      return result;
   }
}

正如您所注意到的,实际操作将通过**invocation.invoke()**调用使用拦截器执行。因此,您可以根据您的要求进行一些预处理和一些后处理。

框架本身通过首次调用 ActionInvocation 对象的 invoke() 来启动该过程。每次调用**invoke()**时,ActionInvocation都会查询其状态并执行接下来出现的拦截器。当所有已配置的拦截器都已调用后,invoke() 方法将导致执行操作本身。

下图通过请求流程显示了相同的概念:

ActionInvocation

创建Action类(Create Action Class)

让我们在**Java 资源 > src**下创建一个名为**com.tutorialspoint.struts2**包的java文件HelloWorldAction.java,内容如下所示。

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class HelloWorldAction extends ActionSupport {
   private String name;

   public String execute() throws Exception {
      System.out.println("Inside action....");
      return "success";
   }  

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

这是我们在前面示例中看到的同一个类。我们具有“name”属性的标准 getter 和 setter 方法,以及返回字符串“success”的 execute 方法。

创建视图(Create a View)

让我们在 Eclipse 项目的 WebContent 文件夹中创建下面的 jsp 文件**HelloWorld.jsp**。

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      Hello World, <s:property value = "name"/>
   </body>
</html>

创建主页(Create Main Page)

我们还需要在 WebContent 文件夹中创建**index.jsp**。此文件将用作初始操作 URL,用户可以单击该 URL 以告诉 Struts 2 框架调用 HelloWorldAction 类的已定义方法并呈现 HelloWorld.jsp 视图。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
   <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      <h1>Hello World From Struts2</h1>
      <form action = "hello">
         <label for = "name">Please enter your name</label><br/>
         <input type = "text" name = "name"/>
         <input type = "submit" value = "Say Hello"/>
      </form>
   </body>
</html>

上面视图文件中定义的**hello**操作将使用 struts.xml 文件映射到 HelloWorldAction 类及其 execute 方法。

配置文件(Configuration Files)

现在,我们需要注册我们的拦截器,然后像在前面的示例中调用默认拦截器一样调用它。要注册新定义的拦截器,将<interceptors>...</interceptors>标签直接放在**struts.xml**文件的<package>标签下。对于默认拦截器,您可以跳过此步骤,就像我们在前面的示例中所做的那样。但是在这里,让我们注册并使用它,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">

      <interceptors>
         <interceptor name = "myinterceptor"
            class = "com.tutorialspoint.struts2.MyInterceptor" />
      </interceptors>

      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <interceptor-ref name = "params"/>
         <interceptor-ref name = "myinterceptor" />
         <result name = "success">/HelloWorld.jsp</result>
      </action>

   </package>
</struts>

需要注意的是,您可以在<package>标签内注册多个拦截器,同时您可以在<action>标签内调用多个拦截器。您可以使用不同的操作调用相同的拦截器。

web.xml 文件需要在 WebContent 下的 WEB-INF 文件夹中创建,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
   
   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
   
   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

右键单击项目名称,然后单击**导出 > WAR 文件**以创建 WAR 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL **https://127.0.0.1:8080/HelloWorldStruts2/index.jsp**。这将生成以下屏幕:

Hello World Struts 4

现在在给定的文本框中输入任何单词,然后单击“Say Hello”按钮以执行已定义的操作。现在,如果您检查生成的日志,您会在底部找到以下文本:

Pre-Processing
Inside action....
Post-Processing

堆叠多个拦截器

正如您可以想象的那样,必须为每个操作配置多个拦截器很快就会变得极其难以管理。因此,拦截器使用拦截器堆栈进行管理。这是一个示例,直接来自 strutsdefault.xml 文件:

<interceptor-stack name = "basicStack">
   <interceptor-ref name = "exception"/>
   <interceptor-ref name = "servlet-config"/>
   <interceptor-ref name = "prepare"/>
   <interceptor-ref name = "checkbox"/>
   <interceptor-ref name = "params"/>
   <interceptor-ref name = "conversionError"/>
</interceptor-stack>

上述堆栈称为**basicStack**,可在您的配置中使用,如下所示。此配置节点位于<package .../>节点下。每个<interceptor-ref .../>标签都引用在当前拦截器堆栈之前已配置的拦截器或拦截器堆栈。因此,在配置初始拦截器和拦截器堆栈时,确保所有拦截器和拦截器堆栈配置的名称唯一非常重要。

我们已经看到如何将拦截器应用于操作,应用拦截器堆栈没有什么不同。事实上,我们使用完全相同的标签:

<action name = "hello" class = "com.tutorialspoint.struts2.MyAction">
   <interceptor-ref name = "basicStack"/>
   <result>view.jsp</result>
</action

注册“basicStack”将使用 hello 操作注册所有六个拦截器的完整堆栈。需要注意的是,拦截器的执行顺序与它们的配置顺序相同。例如,在上述情况下,异常将首先执行,第二个将是 servlet-config,依此类推。

Struts 2 - 结果和结果类型

如前所述,**<results>**标签在 Struts2 MVC 框架中扮演**视图**的角色。操作负责执行业务逻辑。执行业务逻辑后的下一步是使用**<results>**标签显示视图。

通常,结果会附加一些导航规则。例如,如果操作方法是验证用户身份,则有三种可能的结果。

  • 登录成功
  • 登录失败 - 用户名或密码不正确
  • 帐户已锁定

在这种情况下,操作方法将配置三种可能的结果字符串和三个不同的视图以呈现结果。我们已经在前面的示例中看到了这一点。

但是,Struts2 不会将您与使用 JSP 作为视图技术捆绑在一起。毕竟,MVC 范例的全部目的是保持各层独立且高度可配置。例如,对于 Web2.0 客户端,您可能希望返回 XML 或 JSON 作为输出。在这种情况下,您可以为 XML 或 JSON 创建一个新的结果类型来实现此目的。

Struts 附带许多预定义的**结果类型**,以及我们已经看到的默认结果类型**dispatcher**,它用于调度到 JSP 页面。Struts 允许您使用其他标记语言作为视图技术来呈现结果,流行的选择包括**Velocity、Freemaker、XSLT**和**Tiles**。

调度程序结果类型

**dispatcher**结果类型是默认类型,如果未指定其他结果类型,则使用它。它用于转发到服务器上的 servlet、JSP、HTML 页面等。它使用_RequestDispatcher.forward()_方法。

我们在之前的示例中看到了“简写”版本,我们提供了 JSP 路径作为结果标签的主体。

<result name = "success">
   /HelloWorld.jsp
</result>

我们也可以使用<result...>元素内的<param name = "location">标签指定 JSP 文件,如下所示:

<result name = "success" type = "dispatcher">
   <param name = "location">
      /HelloWorld.jsp
   </param >
</result>

我们还可以提供一个**parse**参数,默认为 true。parse 参数确定是否解析 location 参数以获取 OGNL 表达式。

FreeMaker 结果类型

在此示例中,我们将了解如何使用**FreeMaker**作为视图技术。Freemaker 是一个流行的模板引擎,用于使用预定义的模板生成输出。现在让我们创建一个名为**hello.fm**的 Freemaker 模板文件,内容如下:

Hello World ${name}

上面的文件是一个模板,其中**name**是一个参数,将使用定义的操作从外部传递。您将此文件保存在您的 CLASSPATH 中。

接下来,让我们修改**struts.xml**以指定结果,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">

      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction"
         method = "execute">
         <result name = "success" type = "freemarker">
            <param name = "location">/hello.fm</param>
         </result>
      </action>
      
   </package>

</struts>

让我们保留 HelloWorldAction.java、HelloWorldAction.jsp 和 index.jsp 文件,就像我们在示例章节中创建它们一样。

现在右键单击项目名称,然后单击**导出 > WAR 文件**以创建 WAR 文件。

然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL **https://127.0.0.1:8080/HelloWorldStruts2/index.jsp**。这将生成以下屏幕。

Hello World Struts 4

输入值“Struts2”并提交页面。您应该会看到下一页。

Hello World Struts 5

如您所见,这与 JSP 视图完全相同,只是我们没有使用 JSP 作为视图技术。在此示例中,我们使用了 Freemaker。

重定向结果类型

**redirect**结果类型调用标准_response.sendRedirect()_方法,导致浏览器向给定位置创建新请求。

我们可以在<result...>元素的主体中或作为<param name = "location">元素提供位置。Redirect 也支持**parse**参数。这是一个使用 XML 配置的示例:

<action name = "hello" 
   class = "com.tutorialspoint.struts2.HelloWorldAction"
   method = "execute">
   <result name = "success" type = "redirect">
      <param name = "location">
         /NewWorld.jsp
      </param >
   </result>
</action>

只需修改您的 struts.xml 文件以定义上面提到的重定向类型,并创建一个新文件 NewWorld.jpg,每当 hello 操作返回成功时,您将被重定向到该文件。您可以查看Struts 2 重定向操作示例以更好地理解。

Struts 2 - 值栈/OGNL

值栈

值栈是一组多个对象,它们按以下顺序保存以下对象:

序号 对象和说明
1

临时对象

在页面执行期间会创建各种临时对象。例如,在 JSP 标签中循环遍历的集合的当前迭代值。

2

模型对象

如果您在 Struts 应用程序中使用模型对象,则当前模型对象将位于值栈上的操作之前。

3

操作对象

这将是正在执行的当前操作对象。

4

命名对象

这些对象包括 #application、#session、#request、#attr 和 #parameters,并引用相应的 servlet 作用域。

可以通过为 JSP、Velocity 或 Freemarker 提供的标签访问值栈。我们将在单独的章节中学习各种标签,这些标签用于获取和设置 struts 2.0 值栈。您可以在操作中获取 valueStack 对象,如下所示:

ActionContext.getContext().getValueStack()

获得 ValueStack 对象后,可以使用以下方法操作该对象:

序号 ValueStack 方法及描述
1

Object findValue(String expr)

根据默认搜索顺序,通过对栈中表达式求值来查找值。

2

CompoundRoot getRoot()

获取保存推送到栈中对象的 CompoundRoot。

3

Object peek()

获取栈顶对象,但不改变栈。

4

Object pop()

获取栈顶对象并将其从栈中移除。

5void push(Object o)

将此对象放入栈顶。

6

void set(String key, Object o)

使用给定的键设置栈上的对象,以便可以通过 findValue(key,...) 检索。

7

void setDefaultType(Class defaultType)

设置默认类型,如果获取值时未提供类型,则转换为该类型。

8

void setValue(String expr, Object value)

尝试使用默认搜索顺序,使用给定的表达式设置栈中bean上的属性。

9

int size()

获取栈中对象的个数。

OGNL

**对象图导航语言**(OGNL) 是一种强大的表达式语言,用于引用和操作 ValueStack 上的数据。OGNL 还帮助进行数据传输和类型转换。

OGNL 非常类似于 JSP 表达式语言。OGNL 基于在上下文中拥有根对象或默认对象的概念。可以使用井号符号 (#) 表示法引用默认对象或根对象的属性。

如前所述,OGNL 基于上下文,Struts 构建了一个 ActionContext 映射以与 OGNL 一起使用。ActionContext 映射包含以下内容:

  • **Application** - 应用范围的变量

  • **Session** - 会话范围的变量

  • **Root / value stack** - 所有操作变量都存储在此处

  • **Request** - 请求范围的变量

  • **Parameters** - 请求参数

  • **Attributes** - 存储在页面、请求、会话和应用程序范围中的属性

重要的是要理解,Action 对象始终在 ValueStack 中可用。因此,如果您的 Action 对象具有属性 **“x”** 和 **“y”**,则您可以直接使用它们。

ActionContext 中的对象使用井号符号 (#) 引用,但是 ValueStack 中的对象可以直接引用。

例如,如果 **employee** 是 action 类的一个属性,则可以按如下方式引用:

<s:property value = "name"/>

而不是

<s:property value = "#name"/>

如果会话中有一个名为“login”的属性,则可以按如下方式检索它:

<s:property value = "#session.login"/>

OGNL 还支持处理集合——即 Map、List 和 Set。例如,要显示颜色下拉列表,您可以执行以下操作:

<s:select name = "color" list = "{'red','yellow','green'}" />

OGNL 表达式能够巧妙地将“red”、“yellow”、“green”解释为颜色,并基于此构建列表。

在接下来的章节中学习不同的标签时,将广泛使用 OGNL 表达式。因此,与其单独查看它们,不如在表单标签/控件标签/数据标签和 Ajax 标签部分中使用一些示例来查看它们。

ValueStack/OGNL 示例

创建Action

让我们考虑以下 action 类,我们访问 ValueStack,然后设置一些键,我们将在视图(即 JSP 页面)中使用 OGNL 访问这些键。

package com.tutorialspoint.struts2;

import java.util.*; 

import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class HelloWorldAction extends ActionSupport {
   private String name;

   public String execute() throws Exception {
      ValueStack stack = ActionContext.getContext().getValueStack();
      Map<String, Object> context = new HashMap<String, Object>();

      context.put("key1", new String("This is key1")); 
      context.put("key2", new String("This is key2"));
      stack.push(context);

      System.out.println("Size of the valueStack: " + stack.size());
      return "success";
   }  

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

实际上,Struts 2 在执行时将您的 action 添加到 ValueStack 的顶部。因此,将内容放入 ValueStack 的常用方法是为 Action 类中的值添加 getter/setter,然后使用 <s:property> 标签访问这些值。但我向您展示了 ActionContext 和 ValueStack 在 struts 中的工作原理。

创建视图

让我们在 Eclipse 项目的 WebContent 文件夹中创建以下 jsp 文件 **HelloWorld.jsp**。如果 action 返回成功,则将显示此视图:

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      Entered value : <s:property value = "name"/><br/>
      Value of key 1 : <s:property value = "key1" /><br/>
      Value of key 2 : <s:property value = "key2" /> <br/>
   </body>
</html>

我们还需要在 WebContent 文件夹中创建 **index.jsp**,其内容如下:

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
   <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      <h1>Hello World From Struts2</h1>
      <form action = "hello">
         <label for = "name">Please enter your name</label><br/>
         <input type = "text" name = "name"/>
         <input type = "submit" value = "Say Hello"/>
      </form>
   </body>
</html>

配置文件(Configuration Files)

以下是 **struts.xml** 文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">

      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
      </action>

   </package>
</struts>

以下是 **web.xml** 文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
   
   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
   
   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

右键单击项目名称,然后单击 **导出 > WAR 文件** 以创建 WAR 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。

最后,启动 Tomcat 服务器并尝试访问 URL **https://127.0.0.1:8080/HelloWorldStruts2/index.jsp**。这将产生以下屏幕

Hello World Struts 4

现在在给定的文本框中输入任何单词,然后单击“Say Hello”按钮以执行已定义的操作。现在,如果您检查生成的日志,您将在底部找到以下文本:

Size of the valueStack: 3

这将显示以下屏幕,该屏幕将显示您输入的任何值以及我们在 ValueStack 上设置的 key1 和 key2 的值。

Struts 2 - 文件上传

Struts 2 框架提供对使用“基于表单的 HTML 文件上传”处理文件上传的内置支持。上传文件时,它通常会存储在临时目录中,并且您的 Action 类应该处理或将它们移动到永久目录,以确保数据不会丢失。

**注意** - 服务器可能已实施安全策略,禁止您写入临时目录和属于您的 Web 应用程序的目录以外的目录。

Struts 中的文件上传可以通过预定义的拦截器(称为 **FileUpload** 拦截器)实现,该拦截器通过 org.apache.struts2.interceptor.FileUploadInterceptor 类提供,并作为 **defaultStack** 的一部分包含在内。您仍然可以在 struts.xml 中使用它来设置各种参数,如下所示。

创建视图文件

让我们从创建视图开始,这将需要浏览和上传选定的文件。因此,让我们创建一个使用普通 HTML 上传表单的 **index.jsp**,允许用户上传文件:

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>File Upload</title>
   </head>
   
   <body>
      <form action = "upload" method = "post" enctype = "multipart/form-data">
         <label for = "myFile">Upload your file</label>
         <input type = "file" name = "myFile" />
         <input type = "submit" value = "Upload"/>
      </form>
   </body>
</html>

上面的例子需要注意几点。首先,表单的 enctype 设置为 **multipart/form-data**。应该设置此项,以便文件上传拦截器能够成功处理文件上传。下一个需要注意的是表单的操作方法 **upload** 和文件上传字段的名称 - 即 **myFile**。我们需要此信息来创建操作方法和 struts 配置。

接下来,让我们创建一个简单的 jsp 文件 **success.jsp** 来显示文件上传结果(如果成功)。

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>File Upload Success</title>
   </head>
   
   <body>
      You have successfully uploaded <s:property value = "myFileFileName"/>
   </body>
</html>

如果文件上传出现错误,则结果文件 **error.jsp** 如下所示:

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>File Upload Error</title>
   </head>
   
   <body>
      There has been an error in uploading the file.
   </body>
</html>

创建Action类(Create Action Class)

接下来,让我们创建一个名为 **uploadFile.java** 的 Java 类,该类将负责上传文件并将该文件存储在安全位置:

package com.tutorialspoint.struts2;

import java.io.File;
import org.apache.commons.io.FileUtils;
import java.io.IOException; 

import com.opensymphony.xwork2.ActionSupport;

public class uploadFile extends ActionSupport {
   private File myFile;
   private String myFileContentType;
   private String myFileFileName;
   private String destPath;

   public String execute() {
      /* Copy file to a safe location */
      destPath = "C:/apache-tomcat-6.0.33/work/";

      try {
         System.out.println("Src File name: " + myFile);
         System.out.println("Dst File name: " + myFileFileName);
     	    	 
         File destFile  = new File(destPath, myFileFileName);
         FileUtils.copyFile(myFile, destFile);
  
      } catch(IOException e) {
         e.printStackTrace();
         return ERROR;
      }

      return SUCCESS;
   }
   
   public File getMyFile() {
      return myFile;
   }
   
   public void setMyFile(File myFile) {
      this.myFile = myFile;
   }
   
   public String getMyFileContentType() {
      return myFileContentType;
   }
   
   public void setMyFileContentType(String myFileContentType) {
      this.myFileContentType = myFileContentType;
   }
   
   public String getMyFileFileName() {
      return myFileFileName;
   }
   
   public void setMyFileFileName(String myFileFileName) {
      this.myFileFileName = myFileFileName;
   }
}

**uploadFile.java** 是一个非常简单的类。需要注意的是,FileUpload 拦截器与 Parameters 拦截器一起为我们完成了所有繁重的工作。

FileUpload 拦截器默认情况下为我们提供了三个参数。它们使用以下模式命名:

  • **[您的文件名参数]** - 这是用户上传的实际文件。在此示例中,它将是“myFile”

  • **[您的文件名参数]ContentType** - 这是上传文件的类型。在此示例中,它将是“myFileContentType”

  • **[您的文件名参数]FileName** - 这是上传的文件名。在此示例中,它将是“myFileFileName”

感谢 Struts 拦截器,这三个参数对我们可用。我们所要做的就是在我们的 Action 类中创建三个名称正确的参数,这些变量将自动为我们自动连接。因此,在上面的示例中,我们有三个参数和一个操作方法,如果一切顺利,它只返回“success”,否则返回“error”。

配置文件(Configuration Files)

以下是控制文件上传过程的 Struts2 配置属性:

序号 属性及描述
1

struts.multipart.maxSize

作为文件上传接受的文件的最大大小(以字节为单位)。默认为 250M。

2

struts.multipart.parser

用于上传多部分表单的库。默认为 **jakarta**

3

struts.multipart.saveDir

存储临时文件的位置。默认为 javax.servlet.context.tempdir。

为了更改任何这些设置,您可以在应用程序的 struts.xml 文件中使用 **constant** 标签,就像我更改要上传文件的最大大小一样。

让我们将 **struts.xml** 设置如下:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <constant name = "struts.multipart.maxSize" value = "1000000" />
   <package name = "helloworld" extends = "struts-default">
      <action name = "upload" class = "com.tutorialspoint.struts2.uploadFile">
         <result name = "success">/success.jsp</result>
         <result name = "error">/error.jsp</result>
      </action>
   </package>
</struts>

由于 **FileUpload** 拦截器是拦截器默认堆栈的一部分,因此我们不需要显式配置它。但是,您可以在 <action> 内添加 <interceptor-ref> 标签。fileUpload 拦截器接受两个参数 **(a) maximumSize** 和 **(b) allowedTypes**。

maximumSize 参数设置允许的最大文件大小(默认为大约 2MB)。allowedTypes 参数是一个逗号分隔的已接受内容 (MIME) 类型列表,如下所示:

<action name = "upload" class = "com.tutorialspoint.struts2.uploadFile">
   <interceptor-ref name = "basicStack">
   <interceptor-ref name = "fileUpload">
      <param name = "allowedTypes">image/jpeg,image/gif</param>
   </interceptor-ref>
   <result name = "success">/success.jsp</result>
   <result name = "error">/error.jsp</result>
</action>

以下是 **web.xml** 文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
   
   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
   
   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

现在右键单击项目名称,然后单击 **导出 > WAR 文件** 以创建 WAR 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL **https://127.0.0.1:8080/HelloWorldStruts2/upload.jsp**。这将产生以下屏幕:

Hello World Struts 7

现在使用“浏览”按钮选择文件“Contacts.txt”,然后单击上传按钮,该按钮将文件上传到您的服务器,您应该会看到下一页。您可以检查上传的文件是否已保存在 C:\apache-tomcat-6.0.33\work 中。

Hello World Struts 8

请注意,FileUpload 拦截器会自动删除上传的文件,因此您必须在删除上传的文件之前以编程方式将其保存在某个位置。

错误消息

fileUplaod 拦截器使用多个默认错误消息键:

序号 错误消息键及描述
1

struts.messages.error.uploading

无法上传文件时发生的常规错误。

2

struts.messages.error.file.too.large

上传的文件大小超过 maximumSize 指定的大小时发生。

3

struts.messages.error.content.type.not.allowed

上传的文件与指定的预期内容类型不匹配时发生。

您可以在 **WebContent/WEB-INF/classes/messages.properties** 资源文件中覆盖这些消息的文本。

Struts 2 - 数据库访问

本章将通过简单的步骤教你如何使用Struts 2访问数据库。Struts是一个MVC框架,而不是数据库框架,但它提供了对JPA/Hibernate集成的优秀支持。我们将在后面的章节中讨论Hibernate集成,但在本章中,我们将使用普通的JDBC来访问数据库。

本章的第一步是设置和准备我们的数据库。我使用MySQL作为本例的数据库。我的机器上已安装MySQL,并且我创建了一个名为“struts_tutorial”的新数据库。我创建了一个名为login的表并用一些值填充它。以下是创建和填充表的脚本。

我的MySQL数据库使用默认用户名“root”和密码“root123”。

CREATE TABLE `struts_tutorial`.`login` (
   `user` VARCHAR( 10 ) NOT NULL ,
   `password` VARCHAR( 10 ) NOT NULL ,
   `name` VARCHAR( 20 ) NOT NULL ,
   PRIMARY KEY ( `user` )
) ENGINE = InnoDB;

INSERT INTO `struts_tutorial`.`login` (`user`, `password`, `name`)
VALUES ('scott', 'navy', 'Scott Burgemott');

下一步是下载MySQL Connector jar文件,并将此文件放入项目的WEB-INF\lib文件夹中。完成此操作后,我们就可以创建Action类了。

创建Action

Action类具有与数据库表中的列相对应的属性。我们有user、passwordname作为String属性。在action方法中,我们使用user和password参数来检查用户是否存在,如果存在,我们就在下一个屏幕中显示用户名。

如果用户输入了错误的信息,我们将再次将其发送到登录屏幕。

以下是LoginAction.java文件的内容:

package com.tutorialspoint.struts2;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {

   private String user;
   private String password;
   private String name;

   public String execute() {
      String ret = ERROR;
      Connection conn = null;

      try {
         String URL = "jdbc:mysql://127.0.0.1/struts_tutorial";
         Class.forName("com.mysql.jdbc.Driver");
         conn = DriverManager.getConnection(URL, "root", "root123");
         String sql = "SELECT name FROM login WHERE";
         sql+=" user = ? AND password = ?";
         PreparedStatement ps = conn.prepareStatement(sql);
         ps.setString(1, user);
         ps.setString(2, password);
         ResultSet rs = ps.executeQuery();

         while (rs.next()) {
            name = rs.getString(1);
            ret = SUCCESS;
         }
      } catch (Exception e) {
         ret = ERROR;
      } finally {
         if (conn != null) {
            try {
               conn.close();
            } catch (Exception e) {
            }
         }
      }
      return ret;
   }

   public String getUser() {
      return user;
   }

   public void setUser(String user) {
      this.user = user;
   }

   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

创建主页(Create Main Page)

现在,让我们创建一个JSP文件index.jsp来收集用户名和密码。我们将根据数据库检查用户名和密码。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Login</title>
   </head>
   
   <body>
      <form action = "loginaction" method = "post">
         User:<br/><input type = "text" name = "user"/><br/>
         Password:<br/><input type = "password" name = "password"/><br/>
         <input type = "submit" value = "Login"/>		
      </form>
   </body>
</html>

创建视图

现在让我们创建success.jsp文件,如果action返回SUCCESS,则将调用此文件,但是如果action返回ERROR,我们将有另一个视图文件。

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>Successful Login</title>
   </head>
   
   <body>
      Hello World, <s:property value = "name"/>
   </body>
</html>

如果action返回ERROR,则以下是视图文件error.jsp

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>Invalid User Name or Password</title>
   </head>
   
   <body>
      Wrong user name or password provided.
   </body>
</html>

配置文件(Configuration Files)

最后,让我们使用struts.xml配置文件将所有内容组合在一起,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
   
      <action name = "loginaction" 
         class = "com.tutorialspoint.struts2.LoginAction"
         method = "execute">
         <result name = "success">/success.jsp</result>
         <result name = "error">/error.jsp</result>
      </action>
   
   </package>
</struts>

以下是 **web.xml** 文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
   
   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
   
   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

现在,右键单击项目名称,然后单击导出> WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将生成以下屏幕:

Hello World Struts 9

输入错误的用户名和密码。你应该看到下一页。

Hello World Struts 10

现在输入scott作为用户名,navy作为密码。你应该看到下一页。

Hello World Struts 11

Struts 2 - 发送电子邮件

本章解释如何使用Struts 2应用程序发送电子邮件。

对于此练习,你需要从JavaMail API 1.4.4下载并安装mail.jar,并将mail.jar文件放入你的WEB-INF\lib文件夹中,然后按照创建action、视图和配置文件的标准步骤进行操作。

创建Action

下一步是创建一个负责发送电子邮件的Action方法。让我们创建一个名为Emailer.java的新类,其内容如下。

package com.tutorialspoint.struts2;

import java.util.Properties;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import com.opensymphony.xwork2.ActionSupport;

public class Emailer extends ActionSupport {

   private String from;
   private String password;
   private String to;
   private String subject;
   private String body;

   static Properties properties = new Properties();
   static {
      properties.put("mail.smtp.host", "smtp.gmail.com");
      properties.put("mail.smtp.socketFactory.port", "465");
      properties.put("mail.smtp.socketFactory.class",
         "javax.net.ssl.SSLSocketFactory");
      properties.put("mail.smtp.auth", "true");
      properties.put("mail.smtp.port", "465");
   }

   public String execute() {
      String ret = SUCCESS;
      try {
         Session session = Session.getDefaultInstance(properties,  
            new javax.mail.Authenticator() {
               protected PasswordAuthentication 
               getPasswordAuthentication() {
                  return new 
                  PasswordAuthentication(from, password);
               }
            }
         );

         Message message = new MimeMessage(session);
         message.setFrom(new InternetAddress(from));
         message.setRecipients(Message.RecipientType.TO, 
            InternetAddress.parse(to));
         message.setSubject(subject);
         message.setText(body);
         Transport.send(message);
      } catch(Exception e) {
         ret = ERROR;
         e.printStackTrace();
      }
      return ret;
   }

   public String getFrom() {
      return from;
   }

   public void setFrom(String from) {
      this.from = from;
   }

   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   public String getTo() {
      return to;
   }

   public void setTo(String to) {
      this.to = to;
   }

   public String getSubject() {
      return subject;
   }

   public void setSubject(String subject) {
      this.subject = subject;
   }

   public String getBody() {
      return body;
   }

   public void setBody(String body) {
      this.body = body;
   }

   public static Properties getProperties() {
      return properties;
   }

   public static void setProperties(Properties properties) {
      Emailer.properties = properties;
   }
}

如上面的源代码所示,Emailer.java具有与下面给出的email.jsp页面中的表单属性相对应的属性。这些属性是:

  • From - 发件人的电子邮件地址。由于我们使用的是Google的SMTP,因此我们需要一个有效的gtalk ID。

  • Password - 上述帐户的密码。

  • To - 发送电子邮件给谁?

  • Subject - 电子邮件的主题。

  • Body - 实际的电子邮件正文。

我们没有考虑对上述字段进行任何验证,验证将在下一章中添加。现在让我们看看execute()方法。execute()方法使用javax Mail库使用提供的参数发送电子邮件。如果邮件发送成功,则action返回SUCCESS,否则返回ERROR。

创建主页(Create Main Page)

让我们编写主页面JSP文件index.jsp,它将用于收集上面提到的与电子邮件相关的信息:

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
   <title>Email Form</title>
   </head>
   
   <body>
      <em>The form below uses Google's SMTP server. 
         So you need to enter a gmail username and password
      </em>
      
      <form action = "emailer" method = "post">
         <label for = "from">From</label><br/>
         <input type = "text" name = "from"/><br/>
         <label for = "password">Password</label><br/>
         <input type = "password" name = "password"/><br/>
         <label for = "to">To</label><br/>
         <input type = "text" name = "to"/><br/>
         <label for = "subject">Subject</label><br/>
         <input type = "text" name = "subject"/><br/>
         <label for = "body">Body</label><br/>
         <input type = "text" name = "body"/><br/>
         <input type = "submit" value = "Send Email"/>
      </form>
   </body>
</html>

创建视图

我们将使用JSP文件success.jsp,如果action返回SUCCESS,则将调用此文件,但是如果action返回ERROR,我们将有另一个视图文件。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Email Success</title>
   </head>
   
   <body>
      Your email to <s:property value = "to"/> was sent successfully.
   </body>
</html>

如果action返回ERROR,则以下是视图文件error.jsp

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Email Error</title>
   </head>
   
   <body>
      There is a problem sending your email to <s:property value = "to"/>.
   </body>
</html>

配置文件(Configuration Files)

现在让我们使用struts.xml配置文件将所有内容组合在一起,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">

      <action name = "emailer" 
         class = "com.tutorialspoint.struts2.Emailer"
         method = "execute">
         <result name = "success">/success.jsp</result>
         <result name = "error">/error.jsp</result>
      </action>

   </package>
</struts>

以下是 **web.xml** 文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
   
   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>

   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

现在,右键单击项目名称,然后单击导出> WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将生成以下屏幕:

Email User Input

输入所需的信息并单击发送电子邮件按钮。如果一切顺利,你应该看到以下页面。

Email Successful

Struts 2 - 验证框架

在本章中,我们将深入探讨Struts验证框架。在Struts核心,我们有验证框架,它帮助应用程序在执行action方法之前运行规则以执行验证。

客户端验证通常使用Javascript实现。但是,不应该仅仅依赖于客户端验证。最佳实践建议应在应用程序框架的所有级别引入验证。现在让我们看看在Struts项目中添加验证的两种方法。

在这里,我们将以一个Employee为例,其姓名和年龄应使用一个简单的页面捕获,我们将进行这两个验证以确保用户始终输入姓名和年龄,年龄应在28到65之间。

让我们从示例的主JSP页面开始。

创建主页(Create Main Page)

让我们编写主页面JSP文件index.jsp,它将用于收集上面提到的与Employee相关的信息。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Employee Form</title>
   </head>

   <body>
      <s:form action = "empinfo" method = "post">
         <s:textfield name = "name" label = "Name" size = "20" />
         <s:textfield name = "age" label = "Age" size = "20" />
         <s:submit name = "submit" label = "Submit" align="center" />
      </s:form>
   </body>
</html>

index.jsp使用Struts标签,我们还没有介绍,但我们将在与标签相关的章节中学习它们。但现在,假设s:textfield标签打印一个输入字段,s:submit打印一个提交按钮。我们为每个标签使用了label属性,它为每个标签创建标签。

创建视图

如果定义的action返回SUCCESS,我们将使用JSP文件success.jsp。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Success</title>
   </head>
   
   <body>
      Employee Information is captured successfully.
   </body>
</html>

创建Action

因此,让我们定义一个小的action类Employee,然后添加一个名为validate()的方法,如下所示Employee.java文件。确保你的action类扩展了ActionSupport类,否则你的validate方法将不会执行。

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Employee extends ActionSupport {
   private String name;
   private int age;
   
   public String execute() {
       return SUCCESS;
   }
   
   public String getName() {
       return name;
   }
   
   public void setName(String name) {
       this.name = name;
   }
   
   public int getAge() {
       return age;
   }
   
   public void setAge(int age) {
       this.age = age;
   }

   public void validate() {
      if (name == null || name.trim().equals("")) {
         addFieldError("name","The name is required");
      }
      
      if (age < 28 || age > 65) {
         addFieldError("age","Age must be in between 28 and 65");
      }
   }
}

如上例所示,验证方法检查“Name”字段是否有值。如果没有提供值,我们将为“Name”字段添加一个字段错误,并显示自定义错误消息。其次,我们检查“Age”字段的输入值是否在28到65之间,如果不满足此条件,我们将在验证字段上方添加一个错误。

配置文件(Configuration Files)

最后,让我们使用struts.xml配置文件将所有内容组合在一起,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">

      <action name = "empinfo" 
         class = "com.tutorialspoint.struts2.Employee"
         method = "execute">
         <result name = "input">/index.jsp</result>
         <result name = "success">/success.jsp</result>
      </action>

   </package>
</struts>

以下是 **web.xml** 文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee"
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">

   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>

   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

现在,右键单击项目名称,然后单击导出> WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将生成以下屏幕:

Email User Input

现在不要输入任何必需的信息,只需单击提交按钮。你将看到以下结果:

Error

输入所需的信息,但输入错误的“From”字段,例如将姓名设置为“test”,年龄设置为30,最后单击提交按钮。你将看到以下结果:

Success

此验证如何工作?

当用户按下提交按钮时,Struts 2将自动执行validate方法,如果方法内列出的任何“if”语句为真,Struts 2将调用其addFieldError方法。如果添加了任何错误,则Struts 2将不会继续调用execute方法。相反,Struts 2框架将返回input作为调用action的结果。

因此,当验证失败且Struts 2返回input时,Struts 2框架将重新显示index.jsp文件。由于我们使用了Struts 2表单标签,Struts 2将自动在表单字段上方添加错误消息。

这些错误消息是我们指定的,在addFieldError方法调用中。addFieldError方法需要两个参数。第一个是表单字段名称,错误适用于该名称,第二个是在该表单字段上方显示的错误消息。

addFieldError("name","The name is required");

为了处理input的返回值,我们需要在struts.xml中的action节点中添加以下结果。

<result name = "input">/index.jsp</result>

基于XML的验证

进行验证的第二种方法是将xml文件放在action类旁边。Struts2基于XML的验证提供了更多验证选项,例如电子邮件验证、整数范围验证、表单验证字段、表达式验证、正则表达式验证、必需验证、必需字符串验证、字符串长度验证等。

xml文件需要命名为'[action-class]'-validation.xml'。因此,在本例中,我们创建一个名为Employee-validation.xml的文件,其内容如下:

<!DOCTYPE validators PUBLIC 
   "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
   "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">

<validators>
   <field name = "name">
      <field-validator type = "required">
         <message>
            The name is required.
         </message>
      </field-validator>
   </field>

   <field name = "age">
     <field-validator type = "int">
         <param name = "min">29</param>
         <param name = "max">64</param>
         <message>
            Age must be in between 28 and 65
         </message>
      </field-validator>
   </field>
</validators>

上述XML文件应理想情况下与类文件一起保存在你的CLASSPATH中。让我们使用以下Employee action类,而没有validate()方法:

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Employee extends ActionSupport{
   private String name;
   private int age;
   
   public String execute() {
       return SUCCESS;
   }
   
   public String getName() {
       return name;
   }
   
   public void setName(String name) {
       this.name = name;
   }
   
   public int getAge() {
       return age;
   }
   
   public void setAge(int age) {
       this.age = age;
   }
}

其余设置将与上一个示例相同,现在如果你运行应用程序,它将产生与我们在上一个示例中获得的结果相同的结果。

使用xml文件存储配置的优点是可以将验证与应用程序代码分离。你可以让开发人员编写代码,让业务分析师创建验证xml文件。需要注意的另一点是默认情况下可用的验证器类型。

Struts默认情况下还有很多验证器。常见的验证器包括日期验证器、正则表达式验证器和字符串长度验证器。查看以下链接了解更多详情Struts - 基于XML的验证器

Struts2 - 本地化,国际化 (i18n)

国际化 (i18n) 是规划和实施产品和服务的流程,以便它们可以轻松适应特定地区的语言和文化,这个过程称为本地化。国际化过程称为翻译或本地化启用。

国际化缩写为i18n,因为该词以字母“i”开头,以“n”结尾,第一个i和最后一个n之间有18个字符。

Struts2通过资源包、拦截器和标签库在以下位置提供本地化,即国际化 (i18n) 支持:

  • UI标签

  • 消息和错误。

  • 在action类中。

资源包

Struts2使用资源包为Web应用程序的用户提供多种语言和区域设置选项。你不必担心用不同的语言编写页面。你只需要为每种你想要的语言创建一个资源包。资源包将包含用户语言的标题、消息和其他文本。资源包是包含应用程序默认语言的键/值对的文件。

资源文件的简单命名格式为:

bundlename_language_country.properties

这里,bundlename可以是ActionClass、Interface、SuperClass、Model、Package、全局资源属性。下一部分language_country表示国家/地区区域设置,例如,西班牙语(西班牙)区域设置由es_ES表示,英语(美国)区域设置由en_US表示等,其中你可以省略国家/地区部分(可选)。

当你按其键引用消息元素时,Struts框架将按照以下顺序搜索相应的message bundle:

  • ActionClass.properties
  • Interface.properties
  • SuperClass.properties
  • model.properties
  • package.properties
  • struts.properties
  • global.properties

要开发多种语言的应用程序,你应该维护与这些语言/区域设置相对应的多个属性文件,并根据键/值对定义所有内容。

例如,如果你要为美式英语(默认)、西班牙语和法语开发应用程序,则必须创建三个属性文件。在这里,我将只使用global.properties文件,你也可以使用不同的属性文件来分离不同类型的消息。

  • global.properties - 默认情况下将应用英语(美国)。

  • global_fr.properties - 这将用于法语区域设置。

  • global_es.properties - 这将用于西班牙语区域设置。

访问消息

有几种方法可以访问消息资源,包括getText、text标签、UI标签的key属性以及i18n标签。让我们简要了解一下:

要显示i18n文本,请在属性标签或任何其他标签(例如UI标签)中使用对getText的调用,如下所示:

<s:property value = "getText('some.key')" />

text标签从默认资源包(即struts.properties)检索消息。

<s:text name = "some.key" />

i18n标签将任意资源包推入值栈。i18n标签作用域内的其他标签可以显示来自该资源包的消息。

<s:i18n name = "some.package.bundle">
   <s:text name = "some.key" />
</s:i18n>

大多数UI标签的key属性可用于从资源包生成消息。

<s:textfield key = "some.key" name = "textfieldName"/>

本地化示例

让我们目标是创建上一章中提到的index.jsp的多语言版本。相同的文件将按如下方式编写:

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Employee Form with Multilingual Support</title>
   </head>

   <body>
      <h1><s:text name = "global.heading"/></h1>

      <s:url id = "indexEN" namespace="/" action = "locale" >
         <s:param name = "request_locale" >en</s:param>
      </s:url>
      
      <s:url id = "indexES" namespace="/" action = "locale" >
         <s:param name = "request_locale" >es</s:param>
      </s:url>
      
      <s:url id = "indexFR" namespace="/" action = "locale" >
         <s:param name = "request_locale" >fr</s:param>
      </s:url>

      <s:a href="%{indexEN}" >English</s:a>
      <s:a href="%{indexES}" >Spanish</s:a>
      <s:a href="%{indexFR}" >France</s:a>

      <s:form action = "empinfo" method = "post" namespace = "/">
         <s:textfield name = "name" key = "global.name" size = "20" />
         <s:textfield name = "age" key = "global.age" size = "20" />
         <s:submit name = "submit" key = "global.submit" />
      </s:form>

   </body>
</html>

我们将创建success.jsp文件,该文件将在定义的操作返回SUCCESS时调用。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
	pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Success</title>
   </head>
   
   <body>
      <s:property value = "getText('global.success')" />
   </body>
</html>

这里我们需要创建以下两个操作:(a)第一个操作负责处理Locale并以不同的语言显示相同的index.jsp文件;(b)另一个操作负责提交表单本身。这两个操作都将返回SUCCESS,但我们将根据返回值采取不同的操作,因为这两个操作的目的不同。

负责处理Locale的操作

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Locale extends ActionSupport {
   public String execute() {
       return SUCCESS;
   }
}

提交表单的操作

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Employee extends ActionSupport{
   private String name;
   private int age;
   
   public String execute() {
      return SUCCESS;
   }
   
   public String getName() {
      return name;
   }
   
   public void setName(String name) {
      this.name = name;
   }
   
   public int getAge() {
      return age;
   }
   
   public void setAge(int age) {
      this.age = age;
   }
}

现在让我们创建以下三个global.properties文件并将它们放在CLASSPATH中:

global.properties

global.name = Name
global.age = Age
global.submit = Submit
global.heading = Select Locale
global.success = Successfully authenticated

global_fr.properties

global.name = Nom d'utilisateur 
global.age = l'âge
global.submit = Soumettre des
global.heading = Sé lectionnez Local
global.success = Authentifi	é  avec succès

global_es.properties

global.name = Nombre de usuario
global.age = Edad
global.submit = Presentar
global.heading = seleccionar la configuracion regional
global.success = Autenticado correctamente

我们将创建包含两个操作的struts.xml,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <constant name = "struts.custom.i18n.resources" value = "global" />
   <package name = "helloworld" extends = "struts-default" namespace="/">
      <action name = "empinfo" 
         class = "com.tutorialspoint.struts2.Employee"
         method = "execute">
         <result name = "input">/index.jsp</result>
         <result name = "success">/success.jsp</result>
      </action>
      
      <action name = "locale" 
         class = "com.tutorialspoint.struts2.Locale"
         method = "execute">
         <result name = "success">/index.jsp</result>
      </action>
   </package>

</struts>

以下是 **web.xml** 文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee"
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">

   <display-name>Struts 2</display-name>
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>

   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

现在,右键单击项目名称,然后单击导出> WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将生成以下屏幕:

English Output

现在选择任何一种语言,例如我们选择西班牙语,它将显示以下结果:

Spanish Output

您也可以尝试使用法语。最后,让我们尝试在西班牙语Locale下单击提交按钮,它将显示以下屏幕:

Spanish Success

恭喜,现在您拥有了一个多语言网页,您可以将其网站推广到全球。

Struts 2 - 类型转换

HTTP请求中的所有内容都由协议视为字符串。这包括数字、布尔值、整数、日期、小数等等。但是,在Struts类中,您可以拥有任何数据类型的属性。

Struts 如何为您自动装配属性?

Struts在后台使用各种类型转换器来完成繁重的工作。

例如,如果您的Action类中有一个整数属性,Struts会自动将请求参数转换为整数属性,而无需您执行任何操作。默认情况下,Struts附带许多类型转换器。

如果您使用的是以下列出的任何转换器,则无需担心:

  • 整数、浮点数、双精度数、十进制数
  • 日期和日期时间
  • 数组和集合
  • 枚举
  • 布尔值
  • BigDecimal

有时,当您使用自己的数据类型时,需要添加自己的转换器以使Struts了解如何在显示之前转换这些值。考虑以下POJO类Environment.java

package com.tutorialspoint.struts2;

public class Environment {
   private String name;
   
   public  Environment(String name) {
      this.name = name;
   }
   
   public String getName() {
      return name;
   }
   
   public void setName(String name) {
      this.name = name;
   }
}

这是一个非常简单的类,它有一个名为name的属性,因此这个类没有什么特别的。让我们创建另一个包含系统信息的类-SystemDetails.java

为了这个练习的目的,我将Environment硬编码为“Development”,并将操作系统硬编码为“Windows XP SP3”。

在一个实时项目中,您将从系统配置中获取此信息。

让我们使用以下Action类:

package com.tutorialspoint.struts2;
import com.opensymphony.xwork2.ActionSupport;

public class SystemDetails extends ActionSupport {
   private Environment environment = new Environment("Development");
   private String operatingSystem = "Windows XP SP3";

   public String execute() {
      return SUCCESS;
   }
   
   public Environment getEnvironment() {
      return environment;
   }
   
   public void setEnvironment(Environment environment) {
      this.environment = environment;
   }
   
   public String getOperatingSystem() {
      return operatingSystem;
   }
   
   public void setOperatingSystem(String operatingSystem) {
      this.operatingSystem = operatingSystem;
   }
}

接下来,让我们创建一个简单的JSP文件System.jsp来显示环境和操作系统信息。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>System Details</title>
   </head>
   
   <body>
      Environment: <s:property value = "environment"/><br/>
      Operating System:<s:property value = "operatingSystem"/>
   </body>
</html>

让我们使用struts.xmlsystem.jspSystemDetails.java类连接在一起。

SystemDetails类有一个简单的execute()方法,它返回字符串“SUCCESS”。

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
      
      <action name = "system" 
            class = "com.tutorialspoint.struts2.SystemDetails" 
            method = "execute">
         <result name = "success">/System.jsp</result>
      </action>
   </package>
</struts>
  • 右键单击项目名称,然后单击导出 > WAR文件以创建WAR文件。

  • 然后将此WAR部署到Tomcat的webapps目录。

  • 最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/system.action。这将产生以下屏幕:

System Info

上面输出有什么问题?Struts知道如何显示和转换字符串“Windows XP SP3”和其他内置数据类型,但它不知道如何处理Environment类型的属性。它只是简单地调用了类的toString()方法。

为了解决这个问题,让我们现在为Environment类创建一个并注册一个简单的TypeConverter

创建一个名为EnvironmentConverter.java的类,内容如下:

package com.tutorialspoint.struts2;

import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;

public class EnvironmentConverter extends StrutsTypeConverter {
   @Override
   public Object convertFromString(Map context, String[] values, Class clazz) {
      Environment env = new Environment(values[0]);
      return env;
   }

   @Override
   public String convertToString(Map context, Object value) {
      Environment env  = (Environment) value;
      return env == null ? null : env.getName();
   }	
}

EnvironmentConverter扩展了StrutsTypeConverter类,并通过覆盖两个方法convertFromString()convertToString()来告诉Struts如何将Environment转换为字符串,反之亦然。

现在让我们在应用程序中使用它之前注册此转换器。注册转换器有两种方法。

如果转换器只在一个特定的Action中使用,那么您需要创建一个属性文件,其名称必须为'[action-class]'converstion.properties

在我们的例子中,我们创建一个名为SystemDetails-converstion.properties的文件,其中包含以下注册条目:

environment = com.tutorialspoint.struts2.EnvironmentConverter

在上面的例子中,“environment”是SystemDetails.java类中属性的名称,我们告诉Struts使用EnvironmentConverter来转换此属性。

但是,我们不会这样做,而是将此转换器全局注册,以便可以在整个应用程序中使用它。为此,在WEBINF/classes文件夹中创建一个名为xwork-conversion.properties的属性文件,其中包含以下行:

com.tutorialspoint.struts2.Environment = \
   com.tutorialspoint.struts2.EnvironmentConverter

这只是全局注册转换器,以便Struts在每次遇到Environment类型对象时都可以自动进行转换。现在,如果您重新编译并重新运行程序,那么您将得到更好的输出,如下所示:

System Info

显然,现在结果会更好,这意味着我们的Struts转换器工作正常。

这就是您可以创建多个转换器并根据您的需求注册它们的方式。

Struts 2 - 主题和模板

在开始本章的实际教程之前,让我们看一下https://struts.apache.org给出的几个定义:

序号 术语和描述
1

标签 (TAG)

一小段从JSP、FreeMarker或Velocity内部执行的代码。

2

模板 (TEMPLATE)

一段代码,通常用FreeMarker编写,可以由某些标签(HTML标签)渲染。

3

主题 (THEME)

打包在一起以提供通用功能的模板集合。

我还建议阅读Struts2本地化章节,因为我们将再次使用相同的示例来执行我们的练习。

当您在网页中使用Struts 2标签(例如<s:submit...>,<s:textfield...>等)时,Struts 2框架将生成具有预配置样式和布局的HTML代码。Struts 2带有三个内置主题:

序号 主题和描述
1

SIMPLE主题

一个最小的主题,没有“花里胡哨”的功能。例如,textfield标签渲染HTML <input/>标签,没有标签、验证、错误报告或任何其他格式或功能。

2

XHTML主题

这是Struts 2使用的默认主题,它提供SIMPLE主题提供的所有基础功能,并添加了一些功能,例如HTML的标准两列表格布局、每个HTML的标签、验证和错误报告等。

3

CSS_XHTML主题

此主题提供SIMPLE主题提供的所有基础功能,并添加了一些功能,例如基于CSS的标准两列布局,使用<div>表示HTML Struts标签、每个HTML Struts标签的标签,并根据CSS样式表放置。

如上所述,如果您没有指定主题,则Struts 2将默认使用xhtml主题。例如,此Struts 2 select标签:

<s:textfield name = "name" label = "Name" />

生成以下HTML标记:

<tr>

   <td class="tdLabel">
      <label for = "empinfo_name" class="label">Name:</label>
   </td>
   <td>
      <input type = "text" name = "name" value = "" id = "empinfo_name"/>
   </td>

</tr>

这里empinfo是在struts.xml文件中定义的操作名称。

选择主题

您可以根据Struts 2在标签的基础上指定主题,或者您可以使用以下方法之一来指定Struts 2应该使用哪个主题:

  • 特定标签上的theme属性

  • 标签周围的form标签上的theme属性

  • 名为“theme”的页面范围属性

  • 名为“theme”的请求范围属性

  • 名为“theme”的会话范围属性

  • 名为“theme”的应用程序范围属性

  • struts.properties中的struts.ui.theme属性(默认为xhtml)

以下是如果您想为不同的标签使用不同的主题,在标签级别指定它们的语法:

<s:textfield name = "name" label = "Name" theme="xhtml"/>

因为在每个标签的基础上使用主题并不十分实用,所以我们可以简单地在struts.properties文件中使用以下标签指定规则:

# Standard UI theme
struts.ui.theme = xhtml
# Directory where theme template resides
struts.ui.templateDir = template
# Sets the default template type. Either ftl, vm, or jsp
struts.ui.templateSuffix = ftl

以下是我们在本地化章节中获取的结果,我们使用默认主题,并在struts-default.properties文件中设置了struts.ui.theme = xhtml,该文件默认包含在struts2-core.xy.z.jar文件中。

English Output

主题是如何工作的?

对于给定的主题,每个struts标签都有一个关联的模板,例如s:textfield → text.ftls:password → password.ftl等。

这些模板文件压缩在struts2-core.xy.z.jar文件中。这些模板文件为每个标签保留预定义的HTML布局。

通过这种方式,Struts 2框架使用Sturts标签和关联的模板生成最终的HTML标记代码。

Struts 2 tags + Associated template file = Final HTML markup code.

默认模板是用FreeMarker编写的,它们扩展名为.ftl

您也可以使用Velocity或JSP设计您的模板,并相应地在struts.properties中使用struts.ui.templateSuffixstruts.ui.templateDir设置配置。

创建新的主题

创建新主题最简单的方法是复制任何现有的主题/模板文件并进行必要的修改。

让我们从在WebContent/WEBINF/classes中创建一个名为template的文件夹和一个与我们的新主题同名的子文件夹开始。例如,WebContent/WEB-INF/classes/template/mytheme

从这里,您可以从头开始构建模板,也可以从Struts2发行版复制模板,以便将来可以根据需要修改它们。

我们将修改现有的默认模板xhtml,用于学习目的。现在,让我们将内容从struts2-core-x.y.z.jar/template/xhtml复制到我们的主题目录,并且只修改WebContent/WEBINF/classes/template/mytheme/control.ftl文件。打开control.ftl文件后,将看到以下几行:

<table class="${parameters.cssClass?default('wwFormTable')?html}"<#rt/>
<#if parameters.cssStyle??> style="${parameters.cssStyle?html}"<#rt/>
</#if>
>

让我们将上面的control.ftl文件更改为以下内容:

<table style = "border:1px solid black;">

如果您查看form.ftl,您会发现此文件中使用了control.ftl,但form.ftl是从xhtml主题引用此文件的。因此,让我们将其更改如下:

<#include "/${parameters.templateDir}/xhtml/form-validate.ftl" />
<#include "/${parameters.templateDir}/simple/form-common.ftl" />
<#if (parameters.validate?default(false))>
   onreset = "${parameters.onreset?default('clearErrorMessages(this);\
   clearErrorLabels(this);')}"
   
<#else>
   <#if parameters.onreset??>
      onreset="${parameters.onreset?html}"
   </#if>
</#if>
#include "/${parameters.templateDir}/mytheme/control.ftl" />

我假设您可能不太了解FreeMarker模板语言,但您仍然可以通过查看.ftl文件获得一个很好的了解。

但是,让我们保存上述更改,然后返回我们的本地化示例,并创建包含以下内容的WebContent/WEB-INF/classes/struts.properties文件

# Customized them
struts.ui.theme = mytheme
# Directory where theme template resides
struts.ui.templateDir = template
# Sets the template type to ftl.
struts.ui.templateSuffix = ftl

现在更改之后,右键单击项目名称,然后单击导出 > WAR 文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2。这将生成以下屏幕:

Theme and Template

您可以看到表单组件周围有一个边框,这是我们在从xhtml主题复制后修改主题的结果。如果您在学习FreeMarker方面付出一点努力,那么您将能够非常轻松地创建或修改您的主题。

我希望您现在对Struts 2主题和模板有了基本的了解,对吗?

Struts 2 - 异常处理

Struts提供了一种更简单的方法来处理未捕获的异常并将用户重定向到专用的错误页面。您可以轻松地配置Struts以针对不同的异常使用不同的错误页面。

Struts 通过使用“exception”拦截器简化了异常处理。“exception”拦截器包含在默认堆栈中,因此您无需执行任何额外操作即可配置它。它开箱即用,随时可以使用。

让我们来看一个简单的HelloWorld示例,其中对HelloWorldAction.java文件进行了一些修改。在这里,我们故意在我们的HelloWorldAction操作代码中引入了一个空指针异常。

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class HelloWorldAction extends ActionSupport{
   private String name;

   public String execute(){
      String x = null;
      x = x.substring(0);
      return SUCCESS;
   }
   
   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

让我们保留HelloWorld.jsp的内容如下:

<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      Hello World, <s:property value = "name"/>
   </body>
</html>

以下是index.jsp的内容:

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      <h1>Hello World From Struts2</h1>
      <form action = "hello">
         <label for = "name">Please enter your name</label><br/>
         <input type = "text" name = "name"/>
         <input type = "submit" value = "Say Hello"/>
      </form>
   </body>
</html>

您的struts.xml应如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
     
      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
      </action>

   </package>
</struts>

现在右键单击项目名称,然后单击导出 > WAR 文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将生成以下屏幕:

Hello World Input

输入值“Struts2”并提交页面。您应该看到以下页面:

Exception Output

如上例所示,默认异常拦截器在处理异常方面做得很好。

现在让我们为异常创建一个专用的错误页面。创建一个名为Error.jsp的文件,其内容如下:

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
	pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title></title>
   </head>
   
   <body>
      This is my custom error page
   </body>
</html>

现在让我们配置Struts在发生异常时使用此错误页面。让我们修改struts.xml如下:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
   
      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <exception-mapping exception = "java.lang.NullPointerException"
         result = "error" />
         <result name = "success">/HelloWorld.jsp</result>
         <result name = "error">/Error.jsp</result>
      </action>

   </package>
</struts>

如上例所示,现在我们已将Struts配置为对NullPointerException使用专用的Error.jsp。如果您现在重新运行程序,您现在将看到以下输出:

Hello World Output

除此之外,Struts2框架还带有一个“logging”拦截器来记录异常。通过启用记录器来记录未捕获的异常,我们可以轻松查看堆栈跟踪并找出出错的原因。

全局异常映射

我们已经了解了如何处理特定于操作的异常。我们可以全局设置一个异常,该异常将应用于所有操作。例如,要捕获相同的NullPointerException异常,我们可以在<package...>标记内添加<global-exception-mappings...>标记,并且其<result...>标记应添加到struts.xml文件中的<action...>标记内,如下所示:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
      <global-exception-mappings>
         <exception-mapping exception = "java.lang.NullPointerException"
         result = "error" />
      </global-exception-mappings>

      <action name = "hello" 
         class = "com.tutorialspoint.struts2.HelloWorldAction" 
         method = "execute">
         <result name = "success">/HelloWorld.jsp</result>
         <result name = "error">/Error.jsp</result>
      </action>

   </package>
</struts>

Struts 2 - 注解

如前所述,Struts提供两种配置形式。传统方式是使用struts.xml文件进行所有配置。到目前为止,我们在教程中已经看到了很多这样的例子。另一种配置Struts的方法是使用Java 5注解功能。使用Struts注解,我们可以实现零配置

要在您的项目中开始使用注解,请确保您已将以下jar文件包含在WebContent/WEB-INF/lib文件夹中:

  • struts2-convention-plugin-x.y.z.jar
  • asm-x.y.jar
  • antlr-x.y.z.jar
  • commons-fileupload-x.y.z.jar
  • commons-io-x.y.z.jar
  • commons-lang-x.y.jar
  • commons-logging-x.y.z.jar
  • commons-logging-api-x.y.jar
  • freemarker-x.y.z.jar
  • javassist-.xy.z.GA
  • ognl-x.y.z.jar
  • struts2-core-x.y.z.jar
  • xwork-core.x.y.z.jar

现在,让我们看看如何取消struts.xml文件中可用的配置,并将其替换为注解。

为了解释Struts2中注解的概念,我们必须重新考虑在Struts2验证章节中解释的验证示例。

在这里,我们将以一个员工为例,员工的姓名、年龄将使用一个简单的页面捕获,我们将进行两次验证以确保用户始终输入姓名,并且年龄应在28到65之间。

让我们从示例的主JSP页面开始。

创建主页(Create Main Page)

让我们编写主页面JSP文件index.jsp,用于收集上面提到的与员工相关的信息。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Employee Form</title>
   </head>

   <body>
      
      <s:form action = "empinfo" method = "post">
         <s:textfield name = "name" label = "Name" size = "20" />
         <s:textfield name = "age" label = "Age" size = "20" />
         <s:submit name = "submit" label = "Submit" align="center" />
      </s:form>

   </body>
</html>

index.jsp使用Struts标签,我们还没有介绍,但我们将在与标签相关的章节中学习它们。但就目前而言,只需假设s:textfield标签打印一个输入字段,s:submit打印一个提交按钮。我们为每个标签使用了label属性,它为每个标签创建标签。

创建视图

我们将使用JSP文件success.jsp,该文件将在定义的操作返回SUCCESS的情况下被调用。

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
	pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Success</title>
   </head>
   
   <body>
      Employee Information is captured successfully.
   </body>
</html>

创建Action

这是使用注解的地方。让我们使用注解重新定义操作类Employee,然后添加一个名为validate()的方法,如下所示Employee.java文件。确保您的操作类扩展了ActionSupport类,否则您的validate方法将不会执行。

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import com.opensymphony.xwork2.validator.annotations.*;

@Results({
   @Result(name = "success", Location = "/success.jsp"),
   @Result(name = "input", Location = "/index.jsp")
})
public class Employee extends ActionSupport {
   private String name;
   private int age;

   @Action(value = "/empinfo")
   
   public String execute() {
      return SUCCESS;
   }

   @RequiredFieldValidator( message = "The name is required" )
   
   public String getName() {
      return name;
   }
   
   public void setName(String name) {
      this.name = name;
   }

   @IntRangeFieldValidator(message = "Age must be in between 28 and 65", min = "29", max = "65")
   
   public int getAge() {
      return age;
   }
   
   public void setAge(int age) {
      this.age = age;
   }
}

在这个例子中,我们使用了几个注解。让我逐一介绍一下:

  • 首先,我们包含了Results注解。Results注解是结果的集合。

  • 在results注解下,我们有两个result注解。result注解具有与execute方法的结果相对应的name。它们还包含一个位置,说明应提供与execute()的返回值相对应的哪个视图。

  • 下一个注解是Action注解。它用于修饰execute()方法。Action方法还接收一个值,该值是调用该操作的URL。

  • 最后,我使用了两个validation注解。我在name字段上配置了required字段验证器,在age字段上配置了整数范围验证器。我还为验证指定了自定义消息。

配置文件(Configuration Files)

我们真的不需要struts.xml配置文件,所以让我们删除此文件,让我们检查web.xml文件的内容:

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee"
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">

   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>

   <filter>
      <filter-name>struts2</filter-name>
      
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
      
      <init-param>
         <param-name>struts.devMode</param-name>
         <param-value>true</param-value>
      </init-param>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

现在,右键单击项目名称,然后单击导出> WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URL https://127.0.0.1:8080/HelloWorldStruts2/index.jsp。这将生成以下屏幕:

Email User Input

现在不要输入任何必需的信息,只需单击提交按钮。你将看到以下结果:

Error

输入所需的信息,但输入错误的“From”字段,例如将姓名设置为“test”,年龄设置为30,最后单击提交按钮。你将看到以下结果:

Success

Struts 2 注解类型

Struts 2应用程序可以使用Java 5注解作为XML和Java属性配置的替代方案。您可以查看与不同类别相关的最重要注解的列表:

Struts 2 注解类型.

Struts 2 - 控制标签

Struts 2标签有一组标签,使控制页面执行流程变得容易。

以下是重要的Struts 2控制标签列表:

If和Else标签

这些标签执行每种语言中都存在的基本条件流。

'If'标签本身使用,或与'Else If'标签和/或单个/多个'Else'标签一起使用,如下所示:

<s:if test = "%{false}">
   <div>Will Not Be Executed</div>
</s:if>

<s:elseif test = "%{true}">
   <div>Will Be Executed</div>
</s:elseif>

<s:else>
   <div>Will Not Be Executed</div>
</s:else>

查看详细示例

迭代器标签

迭代器将迭代一个值。可迭代值可以是java.util.Collection或java.util.Iterator文件。在迭代迭代器时,您可以使用Sort标签对结果进行排序,或使用SubSet标签获取列表或数组的子集。

以下示例检索值堆栈上当前对象的getDays()方法的值,并使用它进行迭代。

<s:property/>标签打印出迭代器的当前值。

<s:iterator value = "days">
   <p>day is: <s:property/></p>
</s:iterator>

查看详细示例

合并标签

这些merge标签将两个或多个列表作为参数,并将它们全部合并在一起,如下所示:

<s:merge var = "myMergedIterator">
   <s:param value = "%{myList1}" />
   <s:param value = "%{myList2}" />
   <s:param value = "%{myList3}" />
</s:merge>

<s:iterator value = "%{#myMergedIterator}">
   <s:property />
</s:iterator>

查看详细示例

追加标签

这些append标签将两个或多个列表作为参数,并将它们全部追加在一起,如下所示:

<s:append var = "myAppendIterator">
   <s:param value = "%{myList1}" />
   <s:param value = "%{myList2}" />
   <s:param value = "%{myList3}" />
</s:append>

<s:iterator value = "%{#myAppendIterator}">
   <s:property />
</s:iterator>

查看详细示例

生成器标签

这些generator标签根据提供的val属性生成迭代器。以下生成器标签生成迭代器并使用迭代器标签打印它。

<s:generator val = "%{'aaa,bbb,ccc,ddd,eee'}">
   <s:iterator>
      <s:property /><br/>
   </s:iterator>
</s:generator>

查看详细示例

Struts 2 - 数据标签

Struts 2数据标签主要用于操作页面上显示的数据。以下是重要的数据标签:<从这里开始>

Action标签

此标签允许开发人员通过指定操作名称和可选名称空间直接从JSP页面调用操作。标签的主体内容用于呈现来自Action的结果。除非指定了executeResult参数,否则将忽略在struts.xml中为此操作定义的任何结果处理器。

<div>Tag to execute the action</div>
<br />
<s:action name = "actionTagAction" executeresult = "true" />
<br />
<div>To invokes special method  in action class</div>
<br />
<s:action name = "actionTagAction!specialMethod" executeresult = "true" />

查看详细示例

Include标签

这些include将用于在另一个JSP页面中包含JSP文件。

<-- First Syntax -->
<s:include value = "myJsp.jsp" />

<-- Second Syntax -->
<s:include value = "myJsp.jsp">
   <s:param name = "param1" value = "value2" />
   <s:param name = "param2" value = "value2" />
</s:include>

<-- Third Syntax -->
<s:include value = "myJsp.jsp">
   <s:param name = "param1">value1</s:param>
   <s:param name = "param2">value2</s:param>
</s:include>

查看详细示例

Bean标签

这些bean标签实例化符合JavaBeans规范的类。此标签有一个主体,其中可以包含许多Param元素来设置该类上的任何mutator方法。如果在BeanTag上设置了var属性,它会将实例化的bean放入堆栈的上下文。

<s:bean name = "org.apache.struts2.util.Counter" var = "counter">
   <s:param name = "first" value = "20"/>
   <s:param name = "last" value = "25" />
</s:bean>

查看详细示例

Date标签

这些date标签允许您快速轻松地格式化日期。您可以指定自定义格式(例如,“dd/MM/yyyy hh:mm”),您可以生成易于阅读的符号(例如“2小时14分钟内”),或者您可以只回退到属性文件中键为'struts.date.format'的预定义格式。

<s:date name = "person.birthday" format = "dd/MM/yyyy" />
<s:date name = "person.birthday" format = "%{getText('some.i18n.key')}" />
<s:date name = "person.birthday" nice="true" />
<s:date name = "person.birthday" />

查看详细示例

Param标签

这些param标签可用于参数化其他标签。此标签具有以下两个参数。

  • name(字符串)- 参数的名称

  • value(对象)- 参数的值

<pre>
   <ui:component>
      <ui:param name = "key"     value = "[0]"/>
      <ui:param name = "value"   value = "[1]"/>
      <ui:param name = "context" value = "[2]"/>
   </ui:component>
</pre>

查看详细示例

Property标签

这些property标签用于获取值的属性,如果没有指定,则默认为堆栈顶部。

<s:push value = "myBean">
   <!-- Example 1: -->
   <s:property value = "myBeanProperty" />

   <!-- Example 2: -->TextUtils
   <s:property value = "myBeanProperty" default = "a default value" />
</s:push>

查看详细示例

Push标签

这些push标签用于将值推入堆栈以简化使用。

<s:push value = "user">
   <s:propery value = "firstName" />
   <s:propery value = "lastName" />
</s:push>

查看详细示例

Set标签

这些set标签将值赋给指定范围内的变量。当您希望将变量赋给复杂表达式,然后每次简单地引用该变量而不是复杂表达式时,这很有用。可用的范围是application、session、request、pageaction

<s:set name = "myenv" value = "environment.name"/>
<s:property value = "myenv"/>

查看详细示例

Text标签

这些text标签用于呈现I18n文本消息。

<!-- First Example -->
<s:i18n name = "struts.action.test.i18n.Shop">
   <s:text name = "main.title"/>
</s:i18n>

<!-- Second Example -->
<s:text name = "main.title" />

<!-- Third Examlpe -->
<s:text name = "i18n.label.greetings">
   <s:param >Mr Smith</s:param>
</s:text>

查看详细示例

URL标签

这些url标签用于创建URL。

<-- Example 1 -->
<s:url value = "editGadget.action">
   <s:param name = "id" value = "%{selected}" />
</s:url>

<-- Example 2 -->
<s:url action = "editGadget">
   <s:param name = "id" value = "%{selected}" />
</s:url>

<-- Example 3-->
<s:url includeParams="get">
   <s:param name = "id" value = "%{'22'}" />
</s:url>

查看详细示例

Struts 2 - 表单标签

表单标签列表是Struts UI标签的一个子集。这些标签有助于渲染Struts Web应用程序所需的界面,可分为三类。本章将带您了解所有三种类型的UI标签:

简单UI标签

我们在示例中已经使用了这些标签,本章将回顾它们。让我们来看一个包含几个简单UI标签的简单视图页面email.jsp

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <s:head/>
      <title>Hello World</title>
   </head>
   
   <body>
      <s:div>Email Form</s:div>
      <s:text name = "Please fill in the form below:" />
      
      <s:form action = "hello" method = "post" enctype = "multipart/form-data">
         <s:hidden name = "secret" value = "abracadabra"/>
         <s:textfield key = "email.from" name = "from" />
         <s:password key = "email.password" name = "password" />
         <s:textfield key = "email.to" name = "to" />
         <s:textfield key = "email.subject" name = "subject" />
         <s:textarea key = "email.body" name = "email.body" />
         <s:label for = "attachment" value = "Attachment"/>
         <s:file name = "attachment" accept = "text/html,text/plain" />
         <s:token />
         <s:submit key = "submit" />
      </s:form>
      
   </body>
</html>

如果您了解HTML,那么所有使用的标签都是非常常见的HTML标签,每个标签前面都加上了s:前缀以及不同的属性。当我们执行上述程序时,如果已为所有使用的键设置了正确的映射,则会得到以下用户界面。

Struts Simple UI tags

如所示,s:head生成Struts 2应用程序所需的javascript和样式表元素。

接下来,我们有s:div和s:text元素。s:div用于渲染HTML Div元素。这对于不喜欢混合HTML和Struts标签的人很有用。对于这些人,他们可以选择使用s:div来渲染div。

如所示,s:text用于在屏幕上渲染文本。

接下来是我们熟悉的s:form标签。s:form标签具有一个action属性,用于确定表单提交的位置。因为表单中有一个文件上传元素,所以必须将enctype设置为multipart。否则,可以将其留空。

在表单标签的末尾,我们有s:submit标签。这用于提交表单。提交表单时,所有表单值都将提交到s:form标签中指定的action。

在s:form内部,我们有一个名为secret的隐藏属性。这会在HTML中渲染一个隐藏元素。在本例中,“secret”元素的值为“abracadabra”。此元素对最终用户不可见,用于在不同的视图之间传递状态。

接下来,我们有s:label、s:textfield、s:password和s:textarea标签。这些分别用于渲染标签、输入字段、密码和文本区域。我们在“Struts - 发送电子邮件”示例中已经看到这些标签的实际应用。

这里需要注意的是“key”属性的使用。“key”属性用于从属性文件获取这些控件的标签。我们在Struts 2本地化和国际化章节中已经介绍了此功能。

然后,我们有s:file标签,它渲染一个输入文件上传组件。此组件允许用户上传文件。在此示例中,我们使用了s:file标签的“accept”参数来指定允许上传的文件类型。

最后,我们有s:token标签。token标签生成一个唯一令牌,用于确定表单是否被重复提交。

渲染表单时,会将一个隐藏变量作为令牌值放置。例如,假设令牌为“ABC”。提交此表单时,Struts过滤器会将令牌与存储在会话中的令牌进行比较。如果匹配,则从会话中删除令牌。现在,如果意外地重新提交表单(通过刷新或点击浏览器后退按钮),表单将使用“ABC”作为令牌重新提交。在这种情况下,过滤器会再次将令牌与存储在会话中的令牌进行比较。但由于令牌“ABC”已从会话中删除,因此它将不匹配,Struts过滤器将拒绝请求。

分组UI标签

分组UI标签用于创建单选按钮和复选框。让我们来看一个包含复选框和单选按钮标签的简单视图页面HelloWorld.jsp

<%@ page contentType = "text/html; charset = UTF-8"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>

<html>
   <head>
      <title>Hello World</title>
      <s:head />
   </head>
   
   <body>
      <s:form action = "hello.action">
         <s:radio label = "Gender" name = "gender" list="{'male','female'}" />
         <s:checkboxlist label = "Hobbies" name = "hobbies"
         list = "{'sports','tv','shopping'}" />
      </s:form>
      
   </body>
</html>

当我们执行上述程序时,我们的输出将类似于以下内容:

Struts group UI tags

现在让我们来看一下这个例子。在第一个例子中,我们创建了一个简单的单选按钮,标签为“性别”。name属性对于单选按钮标签是必须的,因此我们指定一个名为“gender”的名称。然后我们将一个列表提供给gender。该列表填充了“male”和“female”值。因此,在输出中,我们得到一个包含两个值的单选按钮。

在第二个例子中,我们创建了一个复选框列表。这是为了收集用户的爱好。用户可以有多个爱好,因此我们使用复选框而不是单选按钮。复选框填充了“sports”、“TV”和“Shopping”列表。这将爱好显示为复选框列表。

选择UI标签

让我们探索Struts提供的Select标签的不同变体。让我们来看一个包含select标签的简单视图页面HelloWorld.jsp

<%@ page contentType = "text/html; charset = UTF-8"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>

<html>
   <head>
      <title>Hello World</title>
      <s:head />
   </head>
   
   <body>
      <s:form action = "login.action">
         <s:select name = "username" label = "Username"
            list = "{'Mike','John','Smith'}" />

         <s:select label = "Company Office" name = "mySelection"
            value = "%{'America'}" list="%{#{'America':'America'}}">
            <s:optgroup label = "Asia" 
               list = "%{#{'India':'India','China':'China'}}" />
            <s:optgroup label = "Europe"
               list="%{#{'UK':'UK','Sweden':'Sweden','Italy':'Italy'}}" />
         </s:select>

         <s:combobox label = "My Sign" name = "mySign"
            list = "#{'aries':'aries','capricorn':'capricorn'}" headerkey = "-1" 
            headervalue = "--- Please Select ---" emptyOption = "true" value = "capricorn" />
         <s:doubleselect label = "Occupation" name = "occupation"
            list = "{'Technical','Other'}" doublename = "occupations2"
            doubleList="top == 'Technical' ? 
            {'I.T', 'Hardware'} : {'Accounting', 'H.R'}" />
      </s:form>
   </body>
</html>

当我们执行上述程序时,我们的输出将类似于以下内容:

Struts select UI tags

现在让我们逐一讲解各个案例。

  • 首先,select标签渲染HTML select框。在第一个示例中,我们创建一个简单的select框,名称为“username”,标签为“username”。select框将填充一个包含Mike、John和Smith名称的列表。

  • 在第二个示例中,我们的公司在美国设有总部。它还在亚洲和欧洲设有全球办事处。我们希望在一个select框中显示办事处,但我们希望按大洲名称对全球办事处进行分组。这就是optgroup派上用场的地方。我们使用s:optgroup标签创建一个新组。我们为该组指定一个标签和一个单独的列表。

  • 在第三个示例中,使用了组合框。组合框是输入字段和select框的组合。用户可以从select框中选择一个值,在这种情况下,输入字段将自动填充用户已选择的值。如果用户直接输入值,则不会选择select框中的任何值。

  • 在我们的示例中,我们列出了星座。select框只列出了四个条目,允许用户输入他的星座,如果它不在列表中。我们还在select框中添加了一个标题条目。headerentry是显示在select框顶部的条目。在我们的例子中,我们想显示“请选择”。如果用户没有选择任何内容,则我们假设值为-1。在某些情况下,我们不希望用户选择空值。在这些情况下,可以将“emptyOption”属性设置为false。最后,在我们的示例中,我们为组合框提供“capricorn”作为默认值。

  • 在第四个示例中,我们有一个双重选择。当您想要显示两个select框时,可以使用双重选择。在第一个select框中选择的值决定了第二个select框中显示的内容。在我们的示例中,第一个select框显示“技术”和“其他”。如果用户选择“技术”,我们将在第二个select框中显示“IT”和“硬件”。否则,我们将显示“会计”和“人力资源”。这可以通过使用示例中所示的“list”和“doubleList”属性来实现。

在上例中,我们进行了比较,以查看顶部select框是否等于“技术”。如果是,则显示“IT”和“硬件”。

我们还需要为顶部框(“name = 'Occupations'”)和底部框(“doubleName = 'occupations2'”)指定名称。

Struts 2 - Ajax标签

Struts使用DOJO框架来实现AJAX标签。首先,要继续此示例,需要将struts2-dojo-plugin-2.2.3.jar添加到类路径。

您可以从Struts 2下载的lib文件夹中获取此文件(C:\struts-2.2.3all\struts-2.2.3\lib\struts2-dojo-plugin-2.2.3.jar)

对于此练习,让我们修改HelloWorld.jsp如下:

<%@ page contentType = "text/html; charset = UTF-8"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<%@ taglib prefix = "sx" uri = "/struts-dojo-tags"%>

<html>
   <head>
      <title>Hello World</title>
      <s:head />
      <sx:head />
   </head>
   
   <body>
      <s:form>
         <sx:autocompleter label = "Favourite Colour"
            list = "{'red','green','blue'}" />
         <br />
         <sx:datetimepicker name = "deliverydate" label = "Delivery Date"
            displayformat = "dd/MM/yyyy" />
         <br />
         <s:url id = "url" value = "/hello.action" />
         <sx:div href="%{#url}" delay="2000">
            Initial Content
         </sx:div>
         <br/>
         <sx:tabbedpanel id = "tabContainer">
            <sx:div label = "Tab 1">Tab 1</sx:div>
            <sx:div label = "Tab 2">Tab 2</sx:div>
         </sx:tabbedpanel>
      </s:form>
   </body>
</html>

运行上述示例时,我们将得到以下输出:

Struts Ajax tags

现在让我们一步一步地讲解这个例子。

首先要注意的是添加了一个新的标签库,前缀为sx。这是(struts-dojo-tags)专门为ajax集成创建的标签库。

然后在HTML头部,我们调用sx:head。这初始化了dojo框架,并使其准备好页面内所有AJAX调用。这一步很重要——如果没有初始化sx:head,您的ajax调用将无法工作。

首先,我们有autocompleter标签。autocompleter标签看起来非常像一个select框。它填充了红色、绿色和蓝色值。但是select框和这个框的区别在于它会自动完成。也就是说,如果您开始输入gr,它将用“green”填充它。除此之外,这个标签与我们之前介绍的s:select标签非常相似。

接下来,我们有一个日期时间选择器。此标签创建一个输入字段,旁边有一个按钮。按下按钮时,将显示一个弹出式日期时间选择器。当用户选择日期时,日期将按照标签属性中指定的格式填充到输入文本中。在我们的示例中,我们指定了dd/MM/yyyy作为日期格式。

接下来,我们创建一个指向system.action文件的url标签,我们在之前的练习中创建了这个文件。它不必是system.action——它可以是您之前创建的任何action文件。然后我们有一个div,其超链接设置为url,延迟设置为2秒。运行时会发生的情况是,“初始内容”将显示2秒,然后div的内容将替换为hello.action执行的内容。

最后,我们有一个简单的选项卡面板,有两个选项卡。选项卡本身就是div,标签为“选项卡1”和“选项卡2”。

值得注意的是,Struts中的AJAX标签集成仍在进行中,并且此集成的成熟度随着每个版本的发布而逐渐提高。

Struts 2和Spring集成

Spring是一个流行的Web框架,它可以轻松地与许多常见的Web任务集成。那么问题是,当我们有Struts 2时,为什么还需要Spring呢?Spring不仅仅是一个MVC框架——它提供了许多Struts中没有的其他功能。

例如:依赖注入,这对于任何框架都非常有用。在本章中,我们将通过一个简单的示例来了解如何将Spring和Struts 2集成在一起。

首先,您需要从Spring安装中将以下文件添加到项目的构建路径。您可以从https://www.springsource.org/download下载并安装最新版本的Spring框架。

  • org.springframework.asm-x.y.z.M(a).jar
  • org.springframework.beans-x.y.z.M(a).jar
  • org.springframework.context-x.y.z.M(a).jar
  • org.springframework.core-x.y.z.M(a).jar
  • org.springframework.expression-x.y.z.M(a).jar
  • org.springframework.web-x.y.z.M(a).jar
  • org.springframework.web.servlet-x.y.z.M(a).jar

最后,将struts2-spring-plugin-x.y.z.jar添加到您的Struts lib目录中的WEB-INF/lib中。如果您使用的是Eclipse,则可能会遇到异常java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

要解决这个问题,您需要进入标记选项卡,然后依次右键单击类依赖项,并进行快速修复以发布/导出所有依赖项。最后,确保标记选项卡下没有依赖冲突。

Struts and Sprint Integration

现在让我们按照如下方式设置Struts-Spring集成的web.xml

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
   id = "WebApp_ID" version = "3.0">
	
   <display-name>Struts 2</display-name>
   
   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>

   <listener>
      <listener-class>
         org.springframework.web.context.ContextLoaderListener
      </listener-class>
   </listener>

   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
</web-app>

这里需要注意的重要一点是我们配置的监听器。ContextLoaderListener是加载spring上下文文件所必需的。Spring的配置文件称为applicationContext.xml文件,它必须与web.xml文件放在同一级别。

让我们创建一个名为User.java的简单action类,它有两个属性:firstName和lastName。

package com.tutorialspoint.struts2;

public class User {
   private String firstName;
   private String lastName;

   public String execute() {
      return "success";
   }

   public String getFirstName() {
      return firstName;
   }

   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }

   public String getLastName() {
      return lastName;
   }

   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
}

现在让我们创建applicationContext.xml spring配置文件并实例化User.java类。如前所述,此文件应位于WEB-INF文件夹下:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean id = "userClass" class = "com.tutorialspoint.struts2.User">
      <property name = "firstName" value = "Michael" />
      <property name = "lastName" value = "Jackson" />
   </bean>
</beans>

如上所示,我们已配置user bean,并将值MichaelJackson注入到bean中。我们还为此bean命名为“userClass”,以便我们可以在其他地方重用它。接下来,让我们在WebContent文件夹中创建User.jsp

<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
   pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <title>Hello World</title>
   </head>
   
   <body>
      <h1>Hello World From Struts2 - Spring integration</h1>

      <s:form>
         <s:textfield name = "firstName" label = "First Name"/><br/>
         <s:textfield name = "lastName" label = "Last Name"/><br/>
      </s:form>
      
   </body>
</html>

User.jsp文件非常简单。它只有一个目的——显示用户对象的firstName和lastName的值。最后,让我们使用struts.xml文件将所有实体放在一起。

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "helloworld" extends = "struts-default">
      <action name = "user" class="userClass" 
         method = "execute">
         <result name = "success">/User.jsp</result>
      </action>
   </package>
</struts>

需要注意的重要一点是,我们使用iduserClass来引用类。这意味着我们正在使用spring为User类进行依赖注入。

现在右键单击项目名称,然后单击导出 > WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URLhttps://127.0.0.1:8080/HelloWorldStruts2/User.jsp。这将产生以下屏幕:

Struts and Spring Integration

我们现在已经看到了如何将两个优秀的框架结合在一起。Struts - Spring集成章节到此结束。

Struts 2 & Tiles集成

在本章中,让我们一起学习将Tiles框架与Struts2集成的步骤。Apache Tiles是一个模板框架,旨在简化Web应用程序用户界面的开发。

首先,我们需要从Apache Tiles网站下载tiles jar文件。您需要将以下jar文件添加到项目的类路径。

  • tiles-api-x.y.z.jar
  • tiles-compat-x.y.z.jar
  • tiles-core-x.y.z.jar
  • tiles-jsp-x.y.z.jar
  • tiles-servlet-x.y.z.jar

除此之外,我们还必须将以下jar文件从struts2下载复制到您的WEB-INF/lib目录。

  • commons-beanutils-x.y.zjar
  • commons-digester-x.y.jar
  • struts2-tiles-plugin-x.y.z.jar

现在让我们按照如下所示设置Struts-Tiles集成的web.xml。这里需要注意两点。首先,我们需要告诉tiles在哪里可以找到tiles配置文件tiles.xml。在本例中,它位于/WEB-INF文件夹下。接下来,我们需要初始化Struts2下载附带的Tiles监听器。

<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns = "http://java.sun.com/xml/ns/javaee" 
   xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee 
   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   id = "WebApp_ID" version = "2.5">
   <display-name>Struts2Example15</display-name>
	
   <context-param>
      <param-name>
         org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG
      </param-name>
      
      <param-value>
         /WEB-INF/tiles.xml
      </param-value>
   </context-param>

   <listener>
      <listener-class>
         org.apache.struts2.tiles.StrutsTilesListener
      </listener-class>
   </listener>

   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

   <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
</web-app>

接下来,让我们在/WEB-INF文件夹下创建tiles.xml,内容如下:

<?xml version = "1.0" Encoding = "UTF-8" ?>

<!DOCTYPE tiles-definitions PUBLIC
   "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
   "http://tiles.apache.org/dtds/tiles-config_2_0.dtd">

<tiles-definitions>

   <definition name = "baseLayout" template="/baseLayout.jsp">
      <put-attribute name = "title"  value = "Template"/>
      <put-attribute name = "banner" value = "/banner.jsp"/>
      <put-attribute name = "menu"   value = "/menu.jsp"/>
      <put-attribute name = "body"   value = "/body.jsp"/>
      <put-attribute name = "footer"   value = "/footer.jsp"/>
   </definition>

   <definition name = "tiger" extends = "baseLayout">
      <put-attribute name = "title"  value = "Tiger"/>
      <put-attribute name = "body"   value = "/tiger.jsp"/>      
   </definition>

   <definition name = "lion" extends = "baseLayout">
      <put-attribute name = "title"  value = "Lion"/>
      <put-attribute name = "body"   value = "/lion.jsp"/>      
   </definition>
  
</tiles-definitions>

接下来,我们在baseLayout.jsp中定义一个基本的骨架布局。它有五个可重用/可覆盖的区域。即标题、横幅、菜单、主体页脚。我们为baseLayout提供默认值,然后创建两个从默认布局扩展的自定义项。tiger布局类似于基本布局,只是它使用tiger.jsp作为其主体,并将文本“Tiger”作为标题。同样,lion布局类似于基本布局,只是它使用lion.jsp作为其主体,并将文本“Lion”作为标题。

让我们看一下各个jsp文件。以下是baseLayout.jsp文件的内容:

<%@ taglib uri = "http://tiles.apache.org/tags-tiles" prefix = "tiles"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset = UTF-8">
      <title>
         <tiles:insertAttribute name = "title" ignore="true" />
      </title>
   </head>

   <body>
      <tiles:insertAttribute name = "banner" /><br/>
      <hr/>
      <tiles:insertAttribute name = "menu" /><br/>
      <hr/>
      <tiles:insertAttribute name = "body" /><br/>
      <hr/>
      <tiles:insertAttribute name = "footer" /><br/>
   </body>
</html>

在这里,我们只构建了一个包含tiles属性的基本HTML页面。我们在需要的地方插入tiles属性。接下来,让我们创建一个banner.jsp文件,内容如下:

<img src="https://tutorialspoint.com/images/tp-logo.gif"/>

menu.jsp文件将包含以下几行,它们是链接——到TigerMenu.action和LionMenu.action struts actions。

<%@taglib uri = "/struts-tags" prefix = "s"%>

<a href = "<s:url action = "tigerMenu"/>" Tiger</a><br>
<a href = "<s:url action = "lionMenu"/>" Lion</a><br>

lion.jsp文件将包含以下内容:

<img src="http://upload.wikimedia.org/wikipedia/commons/d/d2/Lion.jpg"/>
The lion

tiger.jsp文件将包含以下内容:

<img src="http://www.freewebs.com/tigerofdarts/tiger.jpg"/>
The tiger

接下来,让我们创建action类文件MenuAction.java,其中包含以下内容:

package com.tutorialspoint.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class MenuAction extends ActionSupport {
   public String tiger() { return "tiger"; }
   public String lion() { return "lion"; }
}

这是一个非常简单的类。我们声明了两个方法tiger()和lion(),它们分别返回tiger和lion作为结果。让我们在struts.xml文件中将其放在一起:

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

<struts>
   <package name = "default" extends = "struts-default">
      <result-types>
         <result-type name = "tiles" 
         class="org.apache.struts2.views.tiles.TilesResult" />
      </result-types>

      <action name = "*Menu" method = "{1}" 
         class = "com.tutorialspoint.struts2.MenuAction">
         <result name = "tiger" type = "tiles">tiger</result>
         <result name = "lion" type = "tiles">lion</result>
      </action>

   </package>
</struts>

让我们检查一下我们在上述文件中做了什么。首先,我们声明了一种名为“tiles”的新结果类型,因为我们现在使用tiles而不是普通的jsp作为视图技术。Struts2支持Tiles视图结果类型,因此我们创建结果类型“tiles”为“org.apache.struts2.view.tiles.TilesResult”类。

接下来,我们想说,如果请求是/tigerMenu.action,则将用户带到tiger tiles页面;如果请求是/lionMenu.action,则将用户带到lion tiles页面。

我们使用一些正则表达式来实现这一点。在我们的action定义中,我们说任何匹配模式“*Menu”的内容都将由此action处理。匹配的方法将在MenuAction类中调用。也就是说,tigerMenu.action将调用tiger(),lionMenu.action将调用lion()。然后我们需要将结果的结果映射到相应的tiles页面。

现在右键单击项目名称,然后单击导出 > WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URLhttps://127.0.0.1:8080/HelloWorldStruts2/tigerMenu.jsp。这将产生以下屏幕:

Struts and Tiles Integration

同样,如果您转到lionMenu.action页面,您将看到使用相同tiles布局的lion页面。

Struts 2和Hibernate集成

Hibernate是一个高性能的对象/关系持久性和查询服务,它在开源GNU Lesser General Public License (LGPL)下获得许可,可以免费下载。在本章中,我们将学习如何实现Struts 2与Hibernate的集成。如果您不熟悉Hibernate,则可以查看我们的Hibernate教程

数据库设置

在本教程中,我将使用“struts2_tutorial”MySQL数据库。我使用用户名“root”和无密码连接到我的机器上的此数据库。首先,您需要运行以下脚本。此脚本创建一个名为student的新表,并在该表中创建一些记录:

CREATE TABLE IF NOT EXISTS `student` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `first_name` varchar(40) NOT NULL,
   `last_name` varchar(40) NOT NULL,
   `marks` int(11) NOT NULL,
   PRIMARY KEY (`id`)
);

--
-- Dumping data for table `student`
--

INSERT INTO `student` (`id`, `first_name`, `last_name`, `marks`)
   VALUES(1, 'George', 'Kane', 20);
INSERT INTO `student` (`id`, `first_name`, `last_name`, `marks`)
   VALUES(2, 'Melissa', 'Michael', 91);
INSERT INTO `student` (`id`, `first_name`, `last_name`, `marks`)
   VALUES(3, 'Jessica', 'Drake', 21);

Hibernate配置

接下来,让我们创建hibernate.cfg.xml,它是Hibernate的配置文件。

<?xml version = '1.0' encoding = 'utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
   "-//Hibernate/Hibernate Configuration DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
   <session-factory>
      <property name = "hibernate.connection.driver_class">c
         om.mysql.jdbc.Driver
      </property>
      
      <property name = "hibernate.connection.url">
         jdbc:mysql://tutorialspoint.com/struts_tutorial
      </property>
      
      <property name = "hibernate.connection.username">root</property>
      
      <property name = "hibernate.connection.password"></property>
      
      <property name = "hibernate.connection.pool_size">10</property>
      
      <property name = "show_sql">true</property>
      
      <property name = "dialect">
         org.hibernate.dialect.MySQLDialect
      </property>
      
      <property name = "hibernate.hbm2ddl.auto">update</property>
      
      <mapping class = "com.tutorialspoint.hibernate.Student" />
   </session-factory>
</hibernate-configuration> 

让我们一起了解hibernate配置文件。首先,我们声明我们正在使用MySQL驱动程序。然后我们声明用于连接到数据库的jdbc url。然后我们声明连接的用户名、密码和池大小。我们还指示我们希望在日志文件中看到SQL,方法是将“show_sql”设置为true。请阅读Hibernate教程以了解这些属性的含义。

最后,我们将映射类设置为com.tutorialspoint.hibernate.Student,我们将在本章中创建它。

环境设置

接下来,此项目需要大量jar文件。附上所需JAR文件的完整列表的屏幕截图:

Struts and Hibernate Jars

大多数JAR文件都可以作为Struts发行版的一部分获得。如果您安装了诸如glassfish、websphere或jboss之类的应用程序服务器,那么您可以从应用程序服务器的lib文件夹中获取大部分其余jar文件。如果不是,您可以单独下载文件:

其余文件,您应该能够从您的Struts2发行版中获得。

Hibernate类

现在让我们创建Hibernate集成所需的Java类。以下是Student.java的内容:

package com.tutorialspoint.hibernate;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "student")
public class Student {
	
   @Id
   @GeneratedValue
   private int id;
   @Column(name = "last_name")
   private String lastName;
   @Column(name = "first_name")
   private String firstName;
   private int marks;
   
   public int getId() {
    return id;
   }
   
   public void setId(int id) {
    this.id = id;
   }
   
   public String getLastName() {
      return lastName;
   }
   
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
   
   public String getFirstName() {
      return firstName;
   }
   
   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }
   
   public int getMarks() {
      return marks;
   }
   
   public void setMarks(int marks) {
      this.marks = marks;
   }
}

这是一个POJO类,根据Hibernate规范表示student表。它具有属性id、firstName和lastName,它们对应于student表的列名。接下来,让我们按如下方式创建StudentDAO.java文件:

package com.tutorialspoint.hibernate;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.googlecode.s2hibernate.struts2.plugin.\
   annotations.SessionTarget;
import com.googlecode.s2hibernate.struts2.plugin.\
   annotations.TransactionTarget;

public class StudentDAO {
	
   @SessionTarget
   Session session;

   @TransactionTarget
   Transaction transaction;

   @SuppressWarnings("unchecked")
   public List<Student> getStudents() {
      List<Student> students = new ArrayList<Student>();
      
      try {
         students = session.createQuery("from Student").list();
      } catch(Exception e) {
         e.printStackTrace();
      }
      return students;
   }

   public void addStudent(Student student) {
      session.save(student);
   }
}

StudentDAO类是Student类的数据库访问层。它具有列出所有学生以及保存新学生记录的方法。

Action类

以下文件AddStudentAction.java定义了我们的action类。这里有两个action方法:execute()和listStudents()。execute()方法用于添加新的学生记录。我们使用dao的save()方法来实现这一点。

另一个方法listStudents()用于列出学生。我们使用dao的list方法获取所有学生列表。

package com.tutorialspoint.struts2;

import java.util.ArrayList;
import java.util.List;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.tutorialspoint.hibernate.Student;
import com.tutorialspoint.hibernate.StudentDAO;

public class AddStudentAction extends ActionSupport implements ModelDriven<Student> {

   Student student  = new Student();
   List<Student> students = new ArrayList<Student>();
   StudentDAO dao = new StudentDAO();
   @Override
  
  public Student getModel() {
      return student;
   }

   public String execute() {
      dao.addStudent(student);
      return "success";
   }

   public String listStudents() {
      students = dao.getStudents();
      return "success";
   }

   public Student getStudent() {
      return student;
   }

   public void setStudent(Student student) {
      this.student = student;
   }

   public List<Student> getStudents() {
      return students;
   }

   public void setStudents(List<Student> students) {
      this.students = students;
   }
	
}

您会注意到我们正在实现ModelDriven接口。当您的action类处理具体的模型类(例如Student)而不是单个属性(例如firstName、lastName)时,可以使用此方法。ModelAware接口要求您实现一个方法来返回模型。在我们的例子中,我们返回“student”对象。

创建视图文件

现在让我们创建student.jsp视图文件,内容如下:

<%@ page contentType = "text/html; charset = UTF-8"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>

<html>
   <head>
      <title>Hello World</title>
      <s:head />
   </head>
   
   <body>
      <s:form action = "addStudent">
         <s:textfield name = "firstName" label = "First Name"/>
         <s:textfield name = "lastName" label = "Last Name"/>
         <s:textfield name = "marks" label = "Marks"/>
         <s:submit/>
         <hr/>
         
         <table>
            <tr>
               <td>First Name</td>
               <td>Last Name</td>
               <td>Marks</td>
            </tr>
            
            <s:iterator value = "students">	
               <tr>
                  <td><s:property value = "firstName"/></td>
                  <td><s:property value = "lastName"/></td>
                  <td><s:property value = "marks"/></td>
                 </tr>
            </s:iterator>	
         </table>
      </s:form>
   </body>
</html>

student.jsp非常简单。在顶部部分,我们有一个提交到“addStudent.action”的表单。它接收firstName、lastName和marks。因为addStudent action与ModelAware“AddSudentAction”绑定,所以会自动创建一个student bean,并自动填充firstName、lastName和marks的值。

在底部部分,我们遍历学生列表(参见AddStudentAction.java)。我们遍历列表,并在表中显示名字、姓氏和分数的值。

Struts配置

让我们使用struts.xml将其放在一起:

<?xml version = "1.0" Encoding = "UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <constant name = "struts.devMode" value = "true" />
   <package name = "myhibernate" extends = "hibernate-default">

      <action name = "addStudent" method = "execute"
         class = "com.tutorialspoint.struts2.AddStudentAction">
         <result name = "success" type = "redirect">
            listStudents
         </result>
      </action>

      <action name = "listStudents" method = "listStudents"
         class = "com.tutorialspoint.struts2.AddStudentAction">
         <result name = "success">/students.jsp</result>
      </action>

   </package>
</struts>

这里需要注意的重要一点是,我们的包“myhibernate”扩展了名为“hibernate-default”的struts2默认包。然后我们声明两个actions:addStudent和listStudents。addStudent在AddStudentAction类上调用execute(),然后成功后,调用listStudents action方法。

listStudent action方法在AddStudentAction类上调用listStudents(),并使用student.jsp作为视图。

现在,右键单击项目名称,然后单击导出 > WAR文件以创建WAR文件。然后将此WAR部署到Tomcat的webapps目录中。最后,启动Tomcat服务器并尝试访问URLhttps://127.0.0.1:8080/HelloWorldStruts2/student.jsp。这将产生以下屏幕:

Struts and Hibernate Result

在顶部部分,我们得到一个表单来输入新学生记录的值,底部部分列出数据库中的学生。继续添加新学生记录并按提交。每次单击提交时,屏幕都会刷新并显示更新的列表。

广告