Struts2教程

本教程将教你如何用简单和容易的步骤使用ApacheStruts来创建企业级的Javaweb应用程序。

本教程是专门为了需要理解Struts2.x框架及其应用程序的Java程序员设计的。本教程将使你达到专家的中等水平。

在继续本教程之前,你应该很好的理解Java编程语言。对MVC框架和JSP或Servlet有基本的了解是很有帮助的。

更新日期

更新内容

2015-06-01

第一版发布

Struts2基础

基本的MVC架构

模型-视图-控制器或通常被称为MVC,是一种用于开发web应用程序的软件设计模式。模型-视图-控制器模式由以下三个部分组成:

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

在一个特定格式中数据的展示由一个控制器的决定引发来呈现该数据。它们是基于模板系统的脚本,如JSP,ASP,PHP,而且它们很容易与AJAX技术进行集成。

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

Struts2是一个基于MVC的框架。在接下来的章节中,我们会看到如何使用包含Struts2的MVC方法。

概述

Struts2是流行且成熟的基于MVC设计模式的web应用程序框架。Struts2不只是Struts1的版本升级,而它是一个完全重写的Struts架构。

WebWork框架一开始是以Struts框架为基础的,它的目标是提供一个建立在Struts上加强和改进的框架来使开发人员更容易进行web开发。

这里有一些强大的功能,它可能会迫使你考虑Struts2:

以上只是Struts2使它成为企业级框架的前十大功能。

虽然Struts2有一列强大的功能,但我不会忘了提及一些关于Struts2的缺点,它将需要很多改进:

最后一点,一个好的框架应该提供许多不同类型的应用程序可以使用的通用的特性。Struts2是最好的web框架之一,而且被广泛地用于开发Rich互联网应用(RIA)。

环境配置

我们的首要任务是让最小的Struts2应用程序运行。本章将指导你如何准备开发环境来使用Struts2开始你的工作。假设你已经在你的机器上安装了JDK(6+),Tomcat和Eclipse。如果你还没有安装这些组件,然后按照快速通道上给出的步骤:

如果你运行的是Windows,并在C:\jdk1.6.0_20上安装了SDK,你就可以把下面这行写入C:\autoexec.bat文件中。

setPATH=C:\jdk1.6.0_20\bin;%PATH%setJAVA_HOME=C:\jdk1.6.0_20

或者,在WindowsXP/7/8中,你也可以右键单击“我的电脑”,选择“属性”,然后是“高级”,然后是“环境变量”。接下来,你将更新PATH值,并且按下OK按钮。

在Unix(Solaris、Linux等等)上,如果在/usr/local/jdk1.6.0_20上安装SDK,并且使用Cshell命令,你将把下面的内容添加到.cshrc文件中。

setenvPATH/usr/local/jdk1.6.0_20/bin:$PATHsetenvJAVA_HOME/usr/local/jdk1.6.0_20

或者,如果你使用集成开发环境(IDE),如BorlandJBuilder,Eclipse,IntelliJIDEA或者SunONEStudio,编译和运行一个简单的程序,用来确认IDE知道你安装了Java,否则应该根据IDE给定的文档做正确的设置。

在Windows机器上,可以通过执行下列命令启动Tomcat,或者你可以简单地双击startup.bat:

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

在Unix(Solaris、Linux等等)机器上,可以通过执行下列命令启动Tomcat:

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

在Windows机器上,可以通过执行下列命令停止Tomcat:

%CATALINA_HOME%\bin\shutdownorC:\apache-tomcat-5.5.29\bin\shutdown`

在Unix(Solaris、Linux等等)机器上,可以通过执行下列命令停止Tomcat:

$CATALINA_HOME/bin/shutdown.shor/usr/local/apache-tomcat-5.5.29/bin/shutdown.sh

本教程中的所有例子使用EclipseIDE编写。所以我建议你应该在你的机器上安装Eclipse的最新版本。

在windows机器上,可以通过执行以下命令启动Eclipse,或者你可以简单地双击eclipse.exe。

%C:\eclipse\eclipse.exe

在Unix(Solaris和Linux等)上,可以通过执行下面的命令启动Eclipse:

$/usr/local/eclipse/eclipse

启动成功后,如果一切正常,它应该显示下面的结果:

现在如果一切正常,你就可以继续安装你的Struts2框架。下面是在你的机器上下载并安装Struts2的简单步骤。

第二步是在任何位置中解压zip文件,我在Windows7机器上的c:\文件夹中下载并解压struts-2.2.3-all.zip,因此我把所有的jar文件放到了C:\struts-2.2.3\lib中。确保你正确地设置了CLASSPATH变量,否则你将会在运行应用程序时遇到问题。

体系结构

从一个高水平看,Struts2是一个pull-MVC(或MVC2)框架。Struts2的模型-视图-控制器模式由下面的五个核心部件实现:

Struts2与传统的MVC框架稍有不同,其中动作担任模型的角色,而不是控制器的角色,虽然有一些重叠。

上面的图描绘模型,视图和控制器到Struts2高级架构。控制器是由Struts2调度servlet过滤器和拦截器实现的,模型是由动作实现的,视图是由结果类型和结果结合而成的。值栈和OGNL提供共同主线,连接和集成其他组件。

这是Struts2MVC模式的体系结构的概述。我们将在后续章节中更详细的介绍每个组件。

以上面的图为基础,它可以解释Struts2中用户的请求的生命周期,如下所示:

实例

因为你学习了Struts2架构,当你在Struts2web应用程序中点击一个超链接或者提交一个HTML表单时,控制器会收集输入并且发送到一个称作Actions的Java类。当Action执行后,结果选择一个资源来显现响应。资源通常是一个JSP,但是它也可以是一个PDF文件,Excel电子表格或者Javaapplet窗口。

假设你已经建立了开发环境。现在让我们继续建立我们的第一个HelloWorldstruts2项目。这个项目的目的是建立一个Web应用程序,该项目收集用户的姓名,并且在用户名后面显示“HelloWorld”。为了任何的Struct2项目,我们将必须创建四个组件:

序号

组件&描述

1

动作

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

2

拦截器

如果需要,则创建拦截器,或者使用已存在的拦截器。这是控制器的部分。

3

视图

创建一个与用户交互的JSPs,它接受输入并且显示最后的信息。

4

配置文件

创建连接动作,视图和控制器的配置文件。这些文件是struts.xml,web.xml,struts.properties。

我将使用EclipseIDE,所以在一个动态Web项目中所有必需的组件都将会被创建。因此,让我们开始创建一个动态Web项目。

启动你的Eclipse,然后使用File>New>DynamicWebProject,并且输入项目名称为HelloWorldStruts2,根据下面画面中给出的选项设置其他的选项:

在下面的画面中选择所有的默认选项,最后检查GenerateWeb.xmldeploymentdescriptor选项。这将在Eclipse中创建一个动态web项目。现在使用Windows>ShowView>ProjectExplorer,你会看到一些东西在你的项目窗口中,如下所示:

现在从struts2lib文件夹C:\struts-2.2.3\lib中复制下列文件到我们项目的WEB-INF\lib文件夹中。为了做到这个,你可以简单地把所有的下列文件拖拽到WEB-INF\lib文件夹中。

Action类是Struts2应用程序的关键,并且我们在action类中实现大部分的业务逻辑。所以让我们在JavaResources>src下的包名com.tutorialspoint.struts2中创建一个java文件HelloWorldAction.java,下面给出它的内容。

当用户点击一个URL时,Action类响应用户动作。执行一个或多个Action类中的方法并且返回一个字符串结果。基于结果的值,将呈现一个指定的JSP页面。

packagecom.tutorialspoint.struts2;publicclassHelloWorldAction{privateStringname;publicStringexecute()throwsException{return"success";}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

它是一个非常简单的带有一个名为“name”属性的类。对于“name”属性,我们有标准的getter和setter方法,还有返回字符串“success”的执行方法。

Struts2框架将创建一个HelloWorldAction类的对象并且为了响应用户的动作调用执行方法。你把业务逻辑放在执行方法中,最后返回字符串常量。简单地说为了,你将必须实现一个动作类,你可以直接使用这个类名作为你的动作名,或者你也可以使用struts.xml文件映射到其他的名字,如下所示。

我们需要一个JSP提交最后的信息,当一个预定义的动作发生时,这个页面会被Struts2框架调用,这种映射将被定义在struts.xml文件中。所以让我们在Eclipse项目的WebContent文件夹中创建下面的jsp文件HelloWorld.jsp。为了做到这个,在项目资源管理器的WebContent文件夹上单击右键,选择New>JSPFile。

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>HelloWorldHelloWorld,

标签库指令告诉Servlet容器这个页面将使用Struts2的标签,而且这些标签将在s之前。s:property标签显示动作类属性"name>的值,它是由HelloWorldAction类的getName()方法返回的。

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

使用struts.xml文件将把在上面的视图文件中定义的hello动作映射到HelloWorldAction类和它的执行方法中。当用户点击提交按钮时,将引起Struts2框架运行在HelloWorldAction类中定义的执行方法,根据方法的返回值一个适当的视图将被作为响应进行选择和呈现。

我们需要一个将URL,HelloWorldAction类(模型)和HelloWorld.jsp(视图)连结在一起的映射。该映射告诉Struts2框架哪个类将响应用户的动作(URL),这个类的哪个方法将被执行,根据方法返回的字符串结果将呈现什么视图。

因此,让我们创建一个名为struts.xml的文件。由于Struts2需要在类文件夹下展示struts.xml。所以在WebContent/WEB-INF/classes文件夹下创建struts.xml文件。默认的情况下,Eclipse不会创建“classes”文件夹,所以需要自己创建。为了做到这个,在项目资源管理器的WEB-INF文件夹上点击右键,并选择New>Folder。struts.xml中应该像这样:

关于上述配置文件有几点需要注意。在这里我们设置常量struts.devMode为true,是因为我们正工作在程序开发环境中,我们需要看到一些有用的日志信息。然后,我们定义了一个名为helloworld的包。当你想要把你的动作分成一组时,创建一个包是有用的。在我们的例子中,我们命名我们的动作为“hello”,它对应着URL/hello.action和通过HelloWorldAction.class进行备份。当URL/hello.action调用时,HelloWorldAction.class的执行方法是运行的方法。如果执行方法的结果返回“success”,然后我们把HelloWorld.jsp呈现给用户。

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

我们已经指定index.jsp为我们的welcome文件。然后我们已经配置了Struts2的过滤器来运行所有的URL(即任何匹配模式/*的URL)。

你可以启用完整的日志记录功能,而通过在WEB-INF/classes文件夹下创建logging.properties文件使用Struts2工作。在你的属性文件中保留下面两行语句:

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

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

就是这样。我们已经准备好使用Struts2框架来运行我们的HelloWorld应用程序。

输入一个值“Struts2”,并且提交页面。你应该看到下一个页面:

配置

本章将带你学习一个Struts2应用程序必需的基本配置。在这里,我们将看到一些重要的配置文件:web.xml,struts.xml,struts-config.xml和struts.properties,它们将被配置。

老实说你可以使用web.xml和struts.xml配置文件,在前面的章节中你已经看到了我们的例子使用这两个文件进行工作,但是为了你学习知识,我也解释其他文件。

web.xml配置文件是一个J2EE的配置文件,它决定如何用servlet容器来处理HTTP请求的元素。它不是严格意义上的一个Struts2的配置文件,但它是一个Struts2工作时需要被配置的文件。

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

如果你在没有生成配置文件的模板或工具(如Eclipse或Maven2)的帮助下开始,那么web.xml是你需要配置的第一个配置文件。下面是在我们最后一个例子中使用的web.xml文件的内容。

注意,我们映射Struts2的过滤器为/*,而不是/*.action,这意味着所有的urls将被struts的过滤器解析。当我们进入注解这一章时,我们将讨论这个。

struts.xml文件包含配置信息,随着动作的开发,你将会修改这些配置信息。这个文件可以用来重写应用程序的默认设置,例如struts.devMode=false,还有定义在属性文件中的其他设置。这个文件可以在文件夹**WEB-INF/classes**下创建。

让我们来看看在前面的章节中已经解释的HelloWorld例子中创建的struts.xml文件。

属性

描述

name(required)

包的唯一标识符。

extends

这个包是由哪个包扩展的默认情况下,我们使用struts-default作为基础包。

abstract

如果标记为true,对于终端用户消费来说,这个包是不可用的。

namesapce

动作的唯一命名空间。

带着name和value属性的常量标签将被用于重写任何default.properties中定义下面的属性,就如我们刚刚设置struts.devMode属性的过程。设置struts.devMode属性允许我们在日志文件中看到更多的调试信息。

我们定义对应于每一个我们要访问的URL的动作标签,我们定义了一个带有execute()方法的类,每当我们访问相应的URL时,它将被访问。

在动作被执行后,结果决定把得到的哪些返回给浏览器。从动作中返回的字符串应该是一个结果的名称。同上,每次执行动作,结果都被配置,或者都作为一个“global”的结果,包中的每个动作都是可用的。结果有可选的name和type属性。默认名称的值为“success”。

我们还没有重写的其他的配置文件是struts-default.xml。这个文件包含了Struts的标准配置设置,你就不必要为了你的项目的99.99%修改这些设置。为此,我们不打算详细地介绍这个文件。如果你有兴趣,可以在struts2-core-2.2.3.jar文件中有效的default.properties文件里看到它。

struts-config.xml配置文件是在Web客户端中视图和模型组件之间的链接,但是你就不必要为了你的项目的99.99%而修改这些设置。基本配置文件包含下面的主要内容:

拦截器&描述

struts-config

它是配置文件的根节点。

form-beans

它是你把ActionForm子类映射到名称上的位置。你使用这个名字作为ActionForm的别名,贯穿struts-config.xml的其余部分,甚至在JSP页面中。

globalforwards

这个部分把web应用的页面映射到名称上。你可以使用该名称来引用实际的页面。这个避免了在web页面上硬编码URLs。

action-mappings

5

controller

这个部分配置Struts内部,而且很少在实际情况中使用。

6

plug-in

这个部分告诉Struts在哪里找到包含提示和错误信息的属性文件。

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

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

这个配置文件提供了一种改变框架的默认行为的机制。实际上,包含在struts.properties配置文件内的所有属性也可以在web.xml中使用init-param被配置,同样也可以在struts.xml配置文件中使用constant标签。但是如果你喜欢保持事情分离和有更多特定的struts,你就可以在文件夹WEB-INF/classes下创建这个文件。

在这个文件中配置的值将重写在default.properties中配置的默认值,default.properties包含在struts2-core-x.y.z.jar分布中。这里有几个属性,你可能会考虑使用struts.properties文件改变它们:

在这里,任何以井号(#)开始的行会被假定为注释,它将被Struts2忽略。

动作是Struts2框架的核心,因为它们是服务于任何MVC(模型-视图-控制器)的框架。每个URL被映射到一个指定的动作中,它提供了必要的处理逻辑来服务用户的请求。

但是动作也在其他两个重要的能力上起作用。首先,动作在从请求到视图传输数据中起着重要的作用,无论它是一个JSP还是其它的结果类型。第二,动作必须协助框架来确定哪些结果应该呈现给视图,该视图在响应中返回给请求。

对Struts2中动作的唯一要求是必须有一个无参数方法,该方法返回String或结果对象,而且必须是一个POJO。如果无参数方法没有被指定,那么默认的动作是使用execute()方法。

你可以选择扩展ActionSupport类,它实现了包括动作接口的6个接口。动作接口如下所示:

publicinterfaceAction{publicstaticfinalStringSUCCESS="success";publicstaticfinalStringNONE="none";publicstaticfinalStringERROR="error";publicstaticfinalStringINPUT="input";publicstaticfinalStringLOGIN="login";publicStringexecute()throwsException;}

让我们来看看HelloWorld例子中的动作方法:

packagecom.tutorialspoint.Struts2;publicclassHelloWorldAction{privateStringname;publicStringexecute()throwsException{return"success";}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

为了举例说明动作方法控制着视图,让我们对execute方法做下面的修改,并且扩展ActionSupport类,如下所示:

packagecom.tutorialspoint.Struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassHelloWorldActionextendsActionSupport{privateStringname;publicStringexecute()throwsException{if("SECRET".equals(name)){returnSUCCESS;}else{returnERROR;}}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

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

让我们在eclipse项目的WebContent文件夹下创建下面的jsp文件HelloWorld.jsp。为了做到这个,在项目资源管理器的WebContent文件夹上点击右键,并选择New>JSPFile。假如返回的结果是SUCCESS,则这个文件将被调用,SUCCESS是定义在动作接口中的一个字符串常量“success”:

假如返回的结果是ERROR,则下面的文件将被框架调用,ERROR等于一个字符串常量“error”。下面是AccessDenied.jsp的内容。

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>AccessDeniedYouarenotauthorizedtoviewthispage.

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

就是这样,不需要改变web.xml文件,所以我们使用Examples章节中已经创建的同一个web.xml。现在,我们已经准备好使用Struts2框架来运行我们的HelloWorld应用程序。

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

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

你将经常定义一个以上的动作来处理不同的请求,并且给用户提供不同的URL,因此你可以定义不同的类,定义如下所示:

packagecom.tutorialspoint.Struts2;importcom.opensymphony.xwork2.ActionSupport;classMyActionextendsActionSupport{publicstaticStringGOOD=SUCCESS;publicstaticStringBAD=ERROR;}publicclassHelloWorldextendsActionSupport{...publicStringexecute(){if("SECRET".equals(name))returnMyAction.GOOD;returnMyAction.BAD;}...}publicclassSomeOtherClassextendsActionSupport{...publicStringexecute(){returnMyAction.GOOD;}...}

你将在struts.xml文件中配置这些动作,如下所示:

正如你在上述假设的例子中看到的,动作的结果SUCCESS和ERROR是重复的。为了解决这个问题,建议你创建一个包含输出结果的类。

拦截器在概念上和servlet过滤器或JDKs代理类一样。拦截器允许横切功能在动作和框架中单独实现。你可以使用拦截器实现下面的内容:

Struts2框架提供的许多功能都是使用拦截实现的;例如包括异常处理,文件上传,生命周期回调和验证等。事实上,由于Struts2是许多拦截器功能的基础,所以每次动作不是不可能有7个或8个拦截器被分配。

Struts2框架提供了一列开箱即用的拦截器来预先设定和准备使用。下面列出了几个重要的拦截器:

拦截器及描述

alias

允许参数有不同的跨请求的别名。

checkbox

通过为没有被检查的复选框添加一个参数值false来协助管理复选框。

conversionError

把从字符串转化为参数类型的错误信息放置到动作的字段错误中。

createSession

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

debugging

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

execAndWait

当动作在后台执行的时侯,把用户定向到一个中间的等待页面。

7

exception

映射动作抛出的异常到一个结果中,通过重定向允许自动异常处理。

8

fileUpload

有利于简单的文件上传。

9

i18n

在用户的会话期间,跟踪选定的语言环境。

10

logger

通过输出被执行的动作的名称提供简单的日志。

11

params

设置动作的请求参数。

12

prepare

它通常是用来做预处理工作,如设置数据库连接。

13

profile

14

scope

在会话或应用程序的范围中存储和检索动作的状态。

15

ServletConfig

为行动提供了各种基于servlet信息的访问。

16

timer

17

token

为有效的标记检查动作用来防止重复地表单提交。

18

validation

为动作提供了验证支持。

关于上面提到的拦截器的完整信息,请查看Struts2的文档。但是我会告诉你通常如何在你的Struts应用程序中使用一个拦截器。

我们将保留HelloWorldAction.java,web.xml,HelloWorld.jsp和index.jsp文件,因为他们已经在Examples章节被创建了,但是让我们修改struts.xml文件,添加一个拦截器,如下所示:

现在,在给定的文本框中输入任何单词,并且单击SayHello按钮执行已定义的动作。现在,如果你查看生成的日志,就会发现下面的文字:

INFO:Serverstartupin3539ms27/08/20118:40:53PMcom.opensymphony.xwork2.util.logging.commons.CommonsLoggerinfoINFO:Executedaction[//hello!execute]took109ms.

在你的应用程序中使用自定义的拦截器是一种提供横切的应用功能的简洁的方式。创建一个自定义的拦截器是很容易的,需要扩展的接口是下面的Interceptor接口:

publicinterfaceInterceptorextendsSerializable{voiddestroy();voidinit();Stringintercept(ActionInvocationinvocation)throwsException;}

正如名称所显示的,init()方法提供了一种初始化拦截器的方法,而destroy()方法提供了一种清理拦截器的工具。与动作不同的是,拦截器在请求之间被重用,而且需要是线程安全的,尤其是intercept()方法。

ActionInvocation对象提供运行时环境的访问。它允许访问动作本身和方法来调用该动作,并判定动作是否已经被调用。

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

让我们在JavaResources>src文件夹中创建下面的MyInterceptor.java:

packagecom.tutorialspoint.Struts2;importjava.util.*;importcom.opensymphony.xwork2.ActionInvocation;importcom.opensymphony.xwork2.interceptor.AbstractInterceptor;publicclassMyInterceptorextendsAbstractInterceptor{publicStringintercept(ActionInvocationinvocation)throwsException{/*letusdosomepre-processing*/Stringoutput="Pre-Processing";System.out.println(output);/*letuscallactionornextinterceptor*/Stringresult=invocation.invoke();/*letusdosomepost-processing*/output="Post-Processing";System.out.println(output);returnresult;}}

如你注意到的,实际的动作将通过使用拦截器调用invocation.invoke()来执行。所以,你可以根据你的需求做一些预处理和一些后处理。

这个框架本身通过第一次调用ActionInvocation对象的invoke()来启动过程。每次invoke()被调用,ActionInvocation查询它的状态,并且执行接下来的拦截器。当所有已配置的拦截器已经被配置时,invoke()方法将引发这个动作本身被执行。下面的图通过请求流显示了相同的概念:

让我们在JavaResources>src中名为com.tutorialspoint.Struts2的包下创建一个java文件HelloWorldAction.java,它的内容在下面给出。

packagecom.tutorialspoint.Struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassHelloWorldActionextendsActionSupport{privateStringname;publicStringexecute()throwsException{System.out.println("Insideaction....");return"success";}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

我们已经在前面的例子中看到这个相同的类。我们对于“name”属性有标准的getters和setters方法,还有返回字符串“success”的execute方法。

让我们在eclipse项目的WebContent文件夹中创建下面的jsp文件helloWorld.jsp。

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

上面的视图文件中定义的hello动作将使用struts.xml文件映射到HelloWorldAction类和它的execute方法中。

现在,我们需要注册我们的拦截器,然后调用它,就如我们已经在前面的例子中调用了默认的拦截器一样。为了注册一个新定义的拦截器,可以把...标签直接放置在struts.xml文件标签内。对于默认的拦截器,你可以跳过这一步,就像我们在前面的例子中一样。但在这里让我们注册和使用它,如下所示:

应该注意的是,你可以在标签内注册多个拦截器,并且同时你可以在标签内调用多个拦截器。你可以用不同的动作调用相同的拦截器。

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

现在,在给定的文本框中输入任何单词,并且单击SayHello按钮执行已经定义的动作。现在,如果你查看生成的日志,就会在底部发现下面的文字:

Pre-ProcessingInsideaction....Post-Processing

可想而知,为每个动作配置多个拦截器很快就会变得非常难以管理。为此,拦截器用拦截器栈进行管理。这儿有一个例子,直接来自struts-default.xml文件:

上面的栈被称为basicStack,它可以用在你的配置中,如下所示。这个配置节点被放置在节点下。每个标签引用一个拦截器或在当前的拦截器栈之前已配置的拦截器栈。因此,当配置初始的拦截器和拦截器栈时,确保在所有拦截器和拦截器栈配置中这个名称是唯一的,这是非常重要的。

我们已经看到了如何应用拦截器到动作中,应用拦截器栈是没有什么不同的。实际上,我们完全使用相同的标签:

view.jsp

上述注册的“basicStack”将完整地注册所有带有hello动作的6个拦截器。应该指出的是,拦截器按照已配置的顺序执行。例如,在上述情况下,将首先执行异常,第二个执行的是servlet配置等等。

结果类型

正如前面提到的,标签在Struts2的MVC框架中担当视图的角色。动作是负责执行业务逻辑。在执行业务逻辑之后,下一步是使用标签显示视图。

在这种情况下,这个动作方法将使用三种可能的结果字符串被配置,并且有三个不同的视图呈现的结果。我们已经在前面的例子中看到了这个。

但是,Struts2没有阻碍你使用JSP作为视图技术。毕竟MVC范例的整个目的是保持层是独立和高度可配置的。例如,对于一个Web2.0的客户端,你可能想要返回XML或JSON作为输出。在这种情况下,你可以为XML或JSON创建一个新的结果类型,并且实现它。

Struts自带一些预定义的结果类型,无论什么我们已经看到的都是默认的结果类型dispatcher,它是用来调度JSP页面的。Struts允许你为了视图技术使用其它标记语言来呈现结果和流行的选择,包括Velocity,Freemaker,XSLT和Tiles。

调度结果类型是默认的类型,如果没有其他的结果类型被指定,则使用它。它被用来在服务器上转发到一个servlet,JSP,HTML页面,等等。它使用RequestDispatcher.forward()方法。

在我们前面的例子中,我们看到了“shorthand”版本,其中我们提供了JSP路径作为结果标签的主体。

/HelloWorld.jsp

我们也可以在元素内使用一个标签来指定JSP文件,如下所示:

/HelloWorld.jsp

我们也可以提供一个parse参数,默认为true。解析参数决定位置参数是否将被解析为OGNL表达式。

在这个例子中,我们将看到如何使用FreeMaker作为视图技术。Freemaker是一种流行的模板引擎,用于使用预定义的模板来生成输出。让我们创建一个称为hello.fm的Freemaker模板一个文件,它的内容如下所示:

HelloWorld${name}

在这里,上述文件是一个模板,其中name是使用已定义的动作从外部传递的一个参数。你将会在CLASSPATH中保留这个文件。接下来,让我们修改struts.xml来指定结果,如下所示:

输入一个值“Struts2”,并提交该页面,你应该看到下一个页面:

正如你所看到的,这是与JSP视图完全一样的,除了我们不依赖于使用JSP作为视图技术。在这个例子中,我们已经使用Freemaker。

重定向的结果类型调用标准的response.sendRedirect()方法,使浏览器给指定的位置创建一个新的请求。

我们可以在元素的元素体中或作为一个元素来提供位置。重定向也支持parse参数。这儿是一个使用XML配置的例子:

/NewWorld.jsp

因此,仅仅修改你的struts.xml文件来定义如上所提及的重定向类型,并且创建一个新的文件NewWorld.jpg,无论何时hello动作返回success,你将会重定向到那里。

值栈/OGNL

值栈是一个几个对象的集合,根据提供的顺序保持下列的对象:

对象及描述

TemporaryObjects

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

TheModelObject

如果你在struts应用程序中使用模型对象,在动作之前当前模型对象被放置到值栈中。

TheActionObject

它是被执行的当前动作对象。

NamedObjects

这些对象包括#application,#session,#request,#attr和#parameters,并且适用于相应的servlet范围。

值栈可以通过JSP,Velocity或者Freemarker提供的标签来访问。我们在单独章节学到的各种各样的标签被用来获取和设置Struts2.0的值栈。你可以在你的动作中得到值栈对象,如下所示:

ActionContext.getContext().getValueStack()

一旦你有了值栈对象,就可以用下面的方法来操作这个对象:

值栈方法及描述

ObjectfindValue(Stringexpr)

通过用默认的搜索顺序对堆栈计算给定的表达式找到一个值。

CompoundRootgetRoot()

获取CompoundRoot,它保存压入堆栈中的对象。

Objectpeek()

在不改变栈的情况下,获取栈顶的对象。

Objectpop()

获取栈顶的对象,并且把它从栈顶移除。

voidpush(Objecto)

把这个对象放进栈顶中。

voidset(Stringkey,Objecto)

如果一个对象被findValue(key,...)检索,则在堆栈上使用给定的键设置它。

voidsetDefaultType(ClassdefaultType)

当获得值时,如果没有提供任何类型,则设置默认的类型转换。

voidsetValue(Stringexpr,Objectvalue)

试图根据默认的搜索顺序在堆栈上使用给定的表达式设置一个bean的属性。

intsize()

获取堆栈中对象的数量。

对象图形导航语言(OGNL)是一个强大的表达式语言,它是用来在值栈上参考和操作数据。OGNL也有助于数据传输和类型转换。

OGNL与JSP表达式语言非常类似。OGNL是以在上下文中有跟对象或默认对象的概念为基础的。默认对象或根对象的属性可以使用的标记符号英镑来引用。

正如前面所提到的,OGNL是基于上下文的,而Struts使用OGNL来构建ActionContext映射。ActionContext映射由以下组成:

理解这一点很重要,即动作对象在值栈中始终是可用的。所以,因此如果你的动作对象有x和y属性,那么它们是随时可供你使用的。

ActionContext中的对象使用英镑符号来引用,然而,值栈中的对象可以被直接引用,例如如果employee是一个动作类的属性,那么它就可以被引用,如下所示:

代替

如果在会话中有一个名为“login”的属性,你就可以检索它,如下所示:

OGNL还支持处理集合,即Map,List和Set。例如,为了显示颜色的下拉列表,你可以这样做:

OGNL表达式巧妙地解释“red”,“yellow”,“green”为颜色,并且在它的基础上建立一个列表。

当我们学习不同的标签时,OGNL表达式将被广泛使用在接下来的章节中。因此,让我们在Form标签/控制标签/数据标签和Ajax标签中使用一些例子来看它,而不是孤立地看他们。

创建动作

让我们考虑我们访问值栈的下面的动作类,然后设置在视图即JSP页面上使用OGNL访问的几个键。

packagecom.tutorialspoint.struts2;importjava.util.*;importcom.opensymphony.xwork2.util.ValueStack;importcom.opensymphony.xwork2.ActionContext;importcom.opensymphony.xwork2.ActionSupport;publicclassHelloWorldActionextendsActionSupport{privateStringname;publicStringexecute()throwsException{ValueStackstack=ActionContext.getContext().getValueStack();Mapcontext=newHashMap();context.put("key1",newString("Thisiskey1"));context.put("key2",newString("Thisiskey2"));stack.push(context);System.out.println("SizeofthevalueStack:"+stack.size());return"success";}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

实际上,当动作执行时,Struts2将它添加到值栈的顶部。所以,把东西放在值栈中通常的方法是为你的动作类的值添加getter/setter方法,然后使用标签来访问值。但是,我将给你展示在struts中ActionContext和ValueStack如何正确地工作。

让我们在eclipse项目的WebContent文件夹下创建下面的jsp文件HelloWorld.jsp。在动作返回success的情况下,这个视图将被显示:

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>HelloWorldEnteredvalue:
Valueofkey1:
Valueofkey2:

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

下面是struts.xml文件的内容:

下面是web.xml文件的内容:

现在,在给定的文本框中输入任何单词,并且单击“SayHello”按钮执行已经定义的动作。现在,如果你查看生成的日志,就会在底部发现下面的文字:

SizeofthevalueStack:3

无论你输入什么值,它都将显示下面的画面,我们已经把key1和key2的值放入了值栈中。

文件上传

Struts2框架为处理文件上传提供了内置支持,它使用“在HTML中基于表单的文件上传”。当上传一个文件时,它通常会被存储在一个临时目录中,而且它们应该由Action类进行处理或移动到一个永久的目录,用来确保数据不丢失。

注意服务器在恰当的位置可能有一个安全策略,它会禁止你写到除了临时目录以外的目录,而且这个目录属于你的web应用应用程序。

通过预定义的名为文件上传的拦截器,Struts的文件上传是可能的,这个拦截器在org.apache.struts2.interceptor.FileUploadInterceptor类是可用的,而且是defaultStack的一部分。你仍然可以使用在struts.xml中设置各种参数,我们将在下面看到。

让我们开始创建需要浏览和上传选定的文件的视图。因此,让我们创建一个带有简单的HTML上传表单的index.jsp,它允许用户上传文件:

在上面的例子中有几点值得注意。首先,表单的编码类型设置为multipart/form-data。为了使用文件上传拦截器来成功地处理文件上传,它应该被设置。下一个注意点是表单的动作方法upload和文件上传字段的名称是myFile。我们需要这些信息来创建动作方法和struts配置。

接下来让我们创建一个简单的jsp文件success.jsp来显示我们的文件上传的结果,假使它成功地执行。

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>FileUploadSuccessYouhavesuccessfullyuploaded

下面是结果文件error.jsp,假使在上传文件过程中有一些错误:

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>FileUploadErrorTherehasbeenanerrorinuploadingthefile.

接下来让我们创建一个称为uploadFile.java的Java类,它负责上传文件,并且把这个文件存储在一个安全的位置:

uploadFile.java是一个非常简单的类。值得注意的一件重要的事情是文件上传拦截器和参数拦截器为我们处理所有繁重的工作。文件上传拦截器在默认情况下有三个可用的参数。它们被命名为下列模式:

对我们来说,这三个参数是可用的,这要归功于Struts的拦截器。我们要做的是在我们的动作类中用正确的名称创建三个参数,而且这些变量是为我们自动连线的。所以,在上面的例子中,我们有三个参数和一个动作方法,如果一切正常,则简单地返回“success”,否则返回“error”。

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

属性&描述

struts.multipart.maxSize

当一个文件上传时,可以接受的最大的文件大小(以字节为单位)。默认设置是250M。

struts.multipart.parser

用于上传多个表单的库。默认情况下是jakarta。

struts.multipart.saveDir

存储临时文件的位置。默认情况下是javax.servlet.context.tempdir。

为了改变这些设置,你可以使用在你的应用程序struts.xml文件中constant标签,就像我改变上传文件的最大大小的操作。让我们有如下所示的struts.xml:

由于拦截器是拦截器defaultStack的一部分,我们并不需要明确地配置它。但是你可以在里面添加标签。文件上传拦截器需要两个参数:(a)maximumSize(b)allowedTypes。maximumSize参数设置允许的最大文件大小(默认为约2MB)。allowedTypes参数是一个逗号分隔的公认的内容(MIME)类型列表,如下所示:

image/jpeg,image/gif/success.jsp/error.jsp

现在使用浏览按钮选择一个文件“Contacts.txt”,而且单击上传按钮,它将在你的服务器上上传文件,然后你应该看到页面。你可以查看保存在C:\apache-tomcat-6.0.33\work中上传的文件。

注意文件上传拦截器自动删除上传的文件,所以你需要通过编程在某个位置上保存上传的文件,在它被删除之前。

文件上传拦截器使用几个默认的错误消息键:

错误消息键&描述

struts.messages.error.uploading

一个发生在文件无法上传时的通常的错误。

struts.messages.error.file.too.large

发生在上传文件比指定的maximumSize大时。

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

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

你可以重写在WebContent/WEB-INF/classes/messages.properties源文件中消息的内容。

数据库访问

本章将用简单的步骤教你如何使用Struts2来访问数据库。Struts是一个MVC框架,而不是一个数据库框架,但它为JPA/Hibernate集成提供了很好的支持。我们将在后面的章节中看到Hibernate集成,但时在本章中我们将使用普通的JDBC来访问数据库。

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

MYSQL数据库默认的用户名是“root”,密码为“root123”。

CREATETABLE`struts_tutorial`.`login`(`user`VARCHAR(10)NOTNULL,`password`VARCHAR(10)NOTNULL,`name`VARCHAR(20)NOTNULL,PRIMARYKEY(`user`))ENGINE=InnoDB;INSERTINTO`struts_tutorial`.`login`(`user`,`password`,`name`)VALUES('scott','navy','ScottBurgemott');

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

现在,让我们创建success.jsp文件,假如动作返回SUCCESS,该文件将被调用,但是假如动作返回ERROR,我们将有另一个视图。

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>SuccessfulLoginHelloWorld,

下面将是一个视图文件error.jsp,假如动作返回ERROR。

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>InvalidUserNameorPasswordWrongusernameorpasswordprovided.

最后,让我们使用struts.xml配置文件把一切都综合起来,如下所示:

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

现在输入用户名为scott和密码为navy。你应该看到下面的页面:

Struts2-发送邮件

下一步是创建一个发送电子邮件的动作方法。让我们创建一个新类,称为Emailer.java,它的内容如下。

正如上面的源代码中看到的,Emailer.java有对应于下面给出的email.jsp页面中的表单属性的属性。这些属性是:

我们有没有考虑过上述属性的任何验证,验证将会在下一章中添加。现在让我们看看execute()方法。execute()方法通过使用提供的参数用javax邮件库发送一封电子邮件。如果邮件发送成功,动作返回SUCCESS,否则它返回ERROR。

我们将使用JSP文件success.jsp,假如动作返回SUCCESS,它将被调用,但假如动作返回ERROR,我们将有另一个视图。

输入所需的信息,并单击SendEmail按钮。如果一切正常,那么你应该看到下面的页面:

验证

现在我们将观察Struts验证框架如何。在Struts的核心中,我们有验证框架,它能在动作方法执行之前协助应用程序运行规则来执行验证。

客户端验证通常是使用Javascript来实现的。但是不应该单独依赖于客户端验证。最佳的实践建议验证应该引入到应用程序框架的各个层中。现在,让我们来看看两种在我们的Struts项目中添加验证的方式。

这里我们将举一个Employee的例子,使用一个简单的页面来捕获它们的姓名和年龄,我们提出两个验证用来确保总是输入一个名字而且年龄应该是在28和65之间。所以,让我们从例子的主JSP页面开始。

我们将使用JSP文件success.jsp,假如动作返回SUCCESS,该文件将被调用。

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

packagecom.tutorialspoint.struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassEmployeeextendsActionSupport{privateStringname;privateintage;publicStringexecute(){returnSUCCESS;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}publicvoidvalidate(){if(name==null||name.trim().equals("")){addFieldError("name","Thenameisrequired");}if(age<28||age>65){addFieldError("age","Agemustbeinbetween28and65");}}}

如在上面的例子所示,验证方法检查“Name”字段是否有一个值。如果没有值被提供,我们为“Name”字段添加一个带有自定义错误信息的字段错误。其次,我们检查“Age”字段的输入值是否在28和65之间,如果这个条件不符合,我们添加一个上述验证字段错误。

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

输入必需的信息,但是输入了错误的From字段,假如说name为“test”和年龄为30,最后单击Submit按钮。你将看到下面的结果:

当用户按下提交按钮时,Struts2会自动执行验证方法,如果任何列出的if语句内部的方法都是true,那么Struts2调用它的addFieldError方法。如果已添加任何错误,那么Struts2将不会继续调用execute方法。而Struts2框架将返回输入作为调用这个行动的结果。

所以当验证失败和Struts2返回输入时,Struts2框架将重新显示index.jsp文件。由于我们使用了Struts2的表单标签,Struts2将会自动添加只是上述表单字段的错误信息。

这些错误信息是我们在addFieldError方法调用中指定的。addFieldError方法有两个参数。第一个参数是错误应用的表单字段名,第二个参数是显示上述表单字段的错误信息。

addFieldError("name","Thenameisrequired");

为了处理输入的返回值,我们需要在struts.xml添加下面的结果到我们的动作节点。

/index.jsp

验证的第二个方法是通过把一个xml文件紧挨着动作类放置。Struts2基于XML验证提供了更多的验证选择,如电子邮件验证,整数范围验证,表单验证字段,表达式验证,正则表达式验证,请求验证,请求字符串验证,字符串长度的验证,等等。

XML文件需要被命名为'[action-class]'-validation.xml。所以,在我们的例子中,我们创建一个文件,名为Employee-validation.xml,它的内容如下所示:

理想的情况下,上面的XML文件会和类文件一起保存在CLASSPATH中。让我们有不包含validate()方法的Employee动作类,如下所示:

packagecom.tutorialspoint.struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassEmployeeextendsActionSupport{privateStringname;privateintage;publicStringexecute(){returnSUCCESS;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}}

其余的设置将保持,因为它是前面的例子,现在如果你运行这个应用程序,它将会产生相同的结果,我们在前面的例子获得的。

用xml文件来存储配置的优点是允许验证从应用程序代码中分离。你可以让开发人员编写代码和业务分析来创建验证xml文件。需要注意的另一件事情是默认验证类型是可用的。默认情况下,有大量使用Struts的验证器。常见的验证器包括日期验证器,正则表达式验证器和字符串长度验证器。

本地化

国际化(i18n)是规划和实施产品和服务,以便他们可以很容易地适应特定的本地语言和文化的过程,这个过程被称为本地化。国际化过程有时被称为翻译或本地化启用。国际化缩写为i18n,因为这个单词以i开始,以n结束,而且第一个i和最后的n之间有18个字符。

Struts2提供本地化,即国际化(i18n)支持,通过使用下面的资源包,拦截器和标签库:

Struts2使用资源包来给Web应用程序的用户提供多种语言和本地化选项。你不必担心用不同的语言编写网页。你所要做的是为每个你想要的语言创建一个资源包。这个资源包将包含你的用户语言的标题,消息和其他文本。资源包是包含使用你的应用程序的默认语言的键/值对的文件。

对资源文件最简单的命名格式是:

bundlename_language_country.properties

这里,bundlename可以是动作类,接口,超类,模型,包,全局资源属性。接下来的部分语言****_****国家代表国家地区,例如Spanish(Spain)地区用es_ES代表,English(UnitedStates)地区用en_US代表等等。这里你可以跳过可选的国家部分。

当你用它的键引用消息元素时,Struts框架按照下列顺序进行检索相应的消息:

为了用多种语言开发你的应用程序,你将不得不保留多个对应于这些语言/地区的属性文件相,并且根据键/值对定义所有的内容。例如,如果你要为美国英语(默认),西班牙语,和法语开发应用程序,你就必须创建三个属性文件。这里,我将只使用global.properties文件,你可以利用不同的属性文件来隔离不同类型的消息。

有几种方法可以访问信息资源,包括getText,文本标签,UI标签的键属性和国际化标签。让我们来看看他们,如下简单地说:

为了显示i18n的文本,在属性标签或其他任何标签中调用getText,例如UI标签,如下所示:

文本标签检索默认的资源包的信息,即struts.properties

i18n标签把任意资源包放进值栈中。i18n标签范围内的其他标签可以显示该资源包的信息:

大多数UI标签的键属性,可以用来检索一个资源包的消息:

让我们把前一章中用多种语言创建的index.jsp作为目标。相同的文件将被编写,如下所示:

我们将创建success.jsp文件,假如动作返回SUCCESS,该文件将被调用。

这里,我们需要创建下面的两个动作。(a)第一个动作监督区域,显示相同的用不同的语言编写的index.jsp文件(b)另一个行动是为了监督提交表单本身。动作都将返回SUCCESS,但根据返回值,我们会采取不同的动作,因为对两个动作来说我们的目的是不同的:

packagecom.tutorialspoint.struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassLocaleextendsActionSupport{publicStringexecute(){returnSUCCESS;}}

现在,让我们创建下面三个global.properties文件,并且把它们放在CLASSPATH中:

global.name=Nameglobal.age=Ageglobal.submit=Submitglobal.heading=SelectLocaleglobal.success=Successfullyauthenticated

global.name=Nomd'utilisateurglobal.age=l'ageglobal.submit=Soumettredesglobal.heading=SélectionnezLocalglobal.success=Authentifiéavecsuccès

global.name=Nombredeusuarioglobal.age=Edadglobal.submit=Presentarglobal.heading=seleccionarlaconfiguracionregionalglobal.success=Autenticadocorrectamente

我们将创建带有两个动作的struts.xml,如下所示:

下面是web.xml文件中的内容:

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

同样,你可以尝试用法语。最后,当我们使用西班牙语言时,让我们尝试单击Submit按钮,它会显示下面的画面:

恭喜你,现在你有一个多语言的网页,你可以在全局范围内启动你的网站。

类型转换

所有HTTP请求都被视为一个String的协议。它包括数字,布尔值,整数,日期,小数和其他的一切。根据HTTP,将所有类型都看成一个字符串。然而,在Struts类中,你会有任何数据类型的属性。Struts是如何自动装配的属性?

Struts在幕后使用了多种类型转换器用来做繁重的工作。例如,如果在动作类中你有一个整数的属性,那么你不用做任何事情,Struts就会自动转换请求参数到整数属性。默认情况下,Struts附带了一些类型转换器。下面列出了它们中的一些,如果你正在使用它们中的一个,那么你就没有什么可担心的:

有时候,当你使用自己的数据类型时,它需要添加自己的转换来使Struts知道如何在显示之前转换这些值。考虑下面的POJO类Environment.java。

packagecom.tutorialspoint.struts2;publicclassEnvironment{privateStringname;publicEnvironment(Stringname){this.name=name;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

这是一个非常简单的类,它有一个名为name的属性,所以这个类并没有什么特别之处。让我们创建另一个类,它包含关于系统-SystemDetails.java的信息。为了这个练习,我有硬编码环境“Development”和操作系统“WindowsXPSP3”。在实际项目中,你会从系统配置中得到这个信息。所以,让我们有下面的动作类:

packagecom.tutorialspoint.struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassSystemDetailsextendsActionSupport{privateEnvironmentenvironment=newEnvironment("Development");privateStringoperatingSystem="WindowsXPSP3";publicStringexecute(){returnSUCCESS;}publicEnvironmentgetEnvironment(){returnenvironment;}publicvoidsetEnvironment(Environmentenvironment){this.environment=environment;}publicStringgetOperatingSystem(){returnoperatingSystem;}publicvoidsetOperatingSystem(StringoperatingSystem){this.operatingSystem=operatingSystem;}}

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

让我们用struts.xml来同时编写system.jsp和SystemDetails.java类。SystemDetails类有一个简单的execute()方法,它返回字符串“SUCCESS”。

上面的输出有什么错?Struts知道如何显示和转换字符串“WindowsXPSP3”和其他内置数据类型,但是它不知道如何处理环境类型的属性。所以,它仅仅被称为类中的toString()方法。为了解决这个问题,现在,让我们为环境类创建并注册一个简单的TypeConverter。创建一个类,名为EnvironmentConverter.java,内容如下所示。

packagecom.tutorialspoint.struts2;importjava.util.Map;importorg.apache.struts2.util.StrutsTypeConverter;publicclassEnvironmentConverterextendsStrutsTypeConverter{@OverridepublicObjectconvertFromString(Mapcontext,String[]values,Classclazz){Environmentenv=newEnvironment(values[0]);returnenv;}@OverridepublicStringconvertToString(Mapcontext,Objectvalue){Environmentenv=(Environment)value;returnenv==nullnull:env.getName();}}

EnvironmentConverter扩展了StrutsTypeConverter类,告诉Struts如何通过重写两个方法convertFromString()和convertToString()把环境转换为一个字符串,反之亦然。现在,让我们在应用程序中使用这个转换器之前注册它。有两种注册转换器的方法。如果转换器将只用于一个特定的动作中,那么你将需要创建一个属性文件,它需要被命名为'[action-class]'-converstion.properties,所以,在我们的例子中,我们创建一个带有下面的注册入口的文件,名为SystemDetails-converstion.properties:

environment=com.tutorialspoint.struts2.EnvironmentConverter

在上面的例子中,“environment”是SystemDetails.java类中一个属性的名字,我们告诉Struts为了来回转换这个属性要使用EnvironmentConverter。然而,我们不会这样做,相反,我们会在全局范围内注册这个转换器,以便它可以在整个应用程序中使用。为了做到这个,在WEB-INF/classes文件夹下创建一个带有下面句子的属性文件,名为xwork-conversion.properties:

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

它简单地在全局范围内注册转换器,为此每次Struts遇到一个环境类型的对象,就可以自动做转换。现在,如果你重新编译并重新运行该程序,你将会得到更好的输出,如下所示:

显然,现在的结果更好,这意味着我们的Struts转换器工作正常。这就是你可以按你的需求创建多个转换器,并且注册它们后使用。

主题/模板

标签

从JSP,FreeMarker或Velocity内部执行的代码小片段

模板

一小段代码,通常在FreeMarker中编写,并且通过某种标签能够被呈现出来(HTML标签)

主题

打包到一起的模板的集合,用来提供自定义功能的。

我还建议浏览Struts2本地化章节,因为我们会再次采用相同的例子来完成我们的实践。

当你在你的web页面上使用一个Struts2标签时,如等,Struts2框架会生成HTML代码和预配置的样式及布局。Struts2有三种内置的主题:

simpletheme

一个最小的主题,不带有"bellsandwhistles"。例如,文本框标签呈现不带有标记,验证,错误报告,或其他任何格式化或功能的HTML标签。

xhtmltheme

这是Struts2使用的默认的主题,提供了所有简单主题提供的基础,并添加了几个功能,如为HTML添加的标准的两列表布局,为每个HTML添加的标记,验证和错误报告等等。

css_xhtmltheme

这个主题提供了所有简单主题提供的基础,并添加了几个功能,如标准的两列基于CSS的布局,为HTMLStruts标签使用

,为每个HTMLStruts标签添加布局,根据CSS样式表替换。

正如上面提到的,如果你没有指定一个主题,那么Struts2会使用默认的xhtml主题。例如这个Struts2选择标签:

产生如下所示的HTML标记:

Name:

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

你可以在每个Struts2标签基础上选择主题或者你可以使用下述方法中的一个来指定Struts2要用的主题:

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

由于在每个标签上都使用主题是不太实际的,所以简单来说,我们可以使用下列标签在struts.properties文件中指定规则:

##StandardUIthemestruts.ui.theme=xhtml##Directorywherethemetemplateresidesstruts.ui.templateDir=template##Setsthedefaulttemplatetype.Eitherftl,vm,orjspstruts.ui.templateSuffix=ftl

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

Struts2tags+Associatedtemplatefile=FinalHTMLmarkupcode.

默认的模板已经被写入FreeMarker中并且它们有扩展版本.ftl。你可以使用velocity或JSP来设计自己的模板,因此可以使用struts.ui.templateSuffix和struts.ui.templateDir在struts.properties中设置配置。

创建新主题的最简单的方式是复制任何现存的主题/模板文件并做一些必需的修改。所以让我们以在WebContent/WEB-INF/classes中创建一个命名为template的文件夹开始,并创建一个我们的新的主题命名的子文件夹,如WebContent/WEB-INF/classes/template/mytheme。从这里开始,你可以构建模板,或者你可以从Struts2发行版中复制模板并根据需要修改它们。

处于学习的目的,我们要修改一些现存的默认的模板xhtml。所以现在让我们复制struts2-core-x.y.z.jar/template/xhtml的内容到我们的主题目录中并只修改WebContent/WEB-INF/classes/template/mytheme/control.ftl文件。当我们打开control.ftl时,它会有如下所示的行:

<#ifparameters.cssStyle>style="${parameters.cssStylehtml}"<#rt/>>

让我们修改上述文件control.ftl,使得它包含以下内容:

如果你检查form.ftl,你会发现control.ftl正在该文件中使用,而form.ftl文件正在从xhtml主题引用该文件。所以让我们做出如下所示的修改:

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

我假设你不太理解FreeMarker模板语言,但是你仍然能知道通过浏览.ftl文件需要做什么。但是,让我们保存上述改变,然后回到我们的本地化实例中,并创建WebContent/WEB-INF/classes/struts.properties文件,其内容如下所示:

##Customizedthemstruts.ui.theme=mytheme##Directorywherethemetemplateresidesstruts.ui.templateDir=template##Setsthetemplatetypetoftl.struts.ui.templateSuffix=ftl

你可以在表单组件的周围看到一个边界,这是从xhtml主题中复制主题并做出改变的结果。如果你没有学习FreeMarker,那么你可以很容易的创建或修改你的主题。至少现在你对Sturts2主题和模板有一个基本的了解,不是吗?

异常处理

Struts提供了一种简单的方式来处理未捕获的异常,并将用户重定向到一个专门的错误页面。你可以简单的配置Struts,使得不同的异常有不同的错误页面。

Struts通过使用“异常”拦截器来使异常处理变得简单。“异常”拦截器是默认的栈的一部分,所以配置它你不需要做额外的工作。它为你的使用已经做好了准备,开箱即用。让我们看一个简单的HelloWorld实例,在HelloWorldAction.java文件中带有一些修改。在这里我们在HelloWorldAction操作代码中有意的引入一个NullPointer异常。

packagecom.tutorialspoint.struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassHelloWorldActionextendsActionSupport{privateStringname;publicStringexecute(){Stringx=null;x=x.substring(0);returnSUCCESS;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}

让我们将下入内容保存到HelloWorld.jsp中:

以下是index.jsp中的内容:

你的struts.xml文件看起来应该如下所示:

输入值"Struts2"并提交页面。你应该能看到如下所示的页面:

正如上述例子显示的一样,默认的异常拦截器做了许多处理异常的工作。现在让我们为我们的异常创建一个专门的错误页面。创建一个名为Error.jsp的文件,其内容如下所示:

现在让我们配置Struts来在异常的情况下使用这个错误页面。让我们修改struts.xml文件,如下所示:

正如上述例子显示的一样,现在我们已经配置了Struts来为NullPointerException使用专门的Error.jsp.如果现在你重新运行这个程序,你应该能看到如下所示的输出:

此外,Struts2框架会生成一个"logging"拦截器来记录异常。通过使记录器记录未捕获的异常,我们可以很容易的查看栈跟踪并找到哪里出现了错误。

我们已经了解了如何处理操作指定的异常。我们可以设置一个全局异常,能够应用带所有的操作中。例如,为了捕获相同的NullPointerException异常,我们可以在标签内部添加标签且它的标签应该被添加到struts.xml文件的标签内,如下所示:

注释

正如前面所提到的,Struts提供了两种形式的配置。传统的方式是为所有的配置使用struts.xml文件。目前为止,在本教程中我们已经见过了太多这样的例子。另一种配置Struts的方式是使用Java5注释功能。使用struts注释,我们可以实现零配置。

在你的项目中开始使用注释之前,请确保在你的WebContent/WEB-INF/lib文件夹中包含下述jar文件:

现在让我们看看不用struts.xml文件中的可用的配置而是用注释代替,应该怎么做。

为了解释Struts2中只是的概念,我们应该重新考虑在Struts2-验证章节中解释的验证示例。

这里我们采用Employee的例子,它使用一个简单的页面来获取姓名和年龄,并且我们会设置两个验证来确保用户会输入姓名以及年龄在28-65之间。所以让我们从例子中的主JSP页面开始。

我们使用JSP文件success.jsp,当定义的操作返回SUCCESS时,该文件会被调用。

这是注释会被用到的地方。让我们用注释重新定义操作类Employee,然后将如下所示的validate()方法添加到Employee.java文件中。确保你的操作类扩展了ActionSupport类,否则你的验证方法不会被执行。

packagecom.tutorialspoint.struts2;importcom.opensymphony.xwork2.ActionSupport;importorg.apache.struts2.convention.annotation.Action;importorg.apache.struts2.convention.annotation.Result;importorg.apache.struts2.convention.annotation.Results;importcom.opensymphony.xwork2.validator.annotations.*;@Results({@Result(name="success",location="/success.jsp"),@Result(name="input",location="/index.jsp")})publicclassEmployeeextendsActionSupport{privateStringname;privateintage;@Action(value="/empinfo")publicStringexecute(){returnSUCCESS;}@RequiredFieldValidator(message="Thenameisrequired")publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}@IntRangeFieldValidator(message="Agemustbeinbetween28and65",min="29",max="65")publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}}

在这个例子中我们已经使用了几个注释。让我们一个一个的仔细浏览一下:

我们实在不需要struts.xml配置文件,所以让我们删除这个文件并查看**web.xml**文件中的内容:

现在不要键入任何必需的信息,只是点击提交按钮。你会看到如下所示的结果:

键入必需的信息,但是输入一个错误的表单字段,如姓名输入为"test",年龄输入为30,然后点击提交按钮。你会看到如下所示的结果:

4j:struts_annotations_types这页要翻译,直接加在下面,不用单独一页。

@Namespace注解允许在Action类中允许定义一个动作的namespace,而不是基于Zero配置的约定。

@Namespace("/content")publicclassEmployeeextendsActionSupport{...}

@Result注解允许在Action类中定义动作结果,而不是在一个XML文件中。

@Result(name="success",value="/success.jsp")publicclassEmployeeextendsActionSupport{...}

@Results注解为一个Action定义了一组结果。

@Results({@Result(name="success",value="/success.jsp"),@Result(name="error",value="/error.jsp")})publicclassEmployeeextendsActionSupport{...}

@After注解标记一个在主动作方法之后需要调用的动作方法,并且结果被执行。返回值将被忽略。

publicclassEmployeeextendsActionSupport{@AfterpublicvoidisValid()throwsValidationException{//validatemodelobject,throwexceptioniffailed}publicStringexecute(){//performsecureactionreturnSUCCESS;}}

@Before注解标记一个在主动作方法之前需要调用的动作方法,并且结果被执行。返回值将被忽略。

publicclassEmployeeextendsActionSupport{@BeforepublicvoidisAuthorized()throwsAuthenticationException{//authorizerequest,throwexceptioniffailed}publicStringexecute(){//performsecureactionreturnSUCCESS;}}

@BeforeResult注解标记一个在结果之前需要执行的动作方法。返回值将被忽略。

publicclassEmployeeextendsActionSupport{@BeforeResultpublicvoidisValid()throwsValidationException{//validatemodelobject,throwexceptioniffailed}publicStringexecute(){//performactionreturnSUCCESS;}}

这个验证注解检查一个字段是否有任何转换错误,如果转换错误存在,则应用它们。

publicclassEmployeeextendsActionSupport{@ConversionErrorFieldValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true)publicStringgetName(){returnname;}}

这个验证注解检查一个日期字段有一个值在指定的范围内。

publicclassEmployeeextendsActionSupport{@DateRangeFieldValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true,min="2005/01/01",max="2005/12/31")publicStringgetDOB(){returndob;}}

这个验证注解检查一个double字段有一个值在指定的范围内。如果设置的既不是最小的也不是最大的,那么什么都不要做。

publicclassEmployeeextendsActionSupport{@DoubleRangeFieldValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true,minInclusive="0.123",maxInclusive="99.987")publicStringgetIncome(){returnincome;}}

这个验证注解检查一个字段是一个有效的e-mail地址,如果它包含一个非空的字符串。

publicclassEmployeeextendsActionSupport{@EmailValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true)publicStringgetEmail(){returnemail;}}

这个非字段级验证器验证一个提供的正则表达式。

@ExpressionValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true,expression="anOGNLexpression")

这个验证注解检查一个数字字段有一个值在指定的范围内。如果设置的既不是最小的也不是最大的,那么什么都不要做。

publicclassEmployeeextendsActionSupport{@IntRangeFieldValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true,min="0",max="42")publicStringgetAge(){returnage;}}

这个注解使用一个正则表达式来验证一个字符串字段。

@RegexFieldValidator(key="regex.field",expression="yourregexp")

这个验证注解检查一个字段是非空的。这个注解必须应用在方法层。

publicclassEmployeeextendsActionSupport{@RequiredFieldValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true)publicStringgetAge(){returnage;}}

这个验证注解检查一个字符串字段不是空的(即非空,长度>0)。

publicclassEmployeeextendsActionSupport{@RequiredStringValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true,trim=true)publicStringgetName(){returnname;}}

这个验证器检查一个字符串字段是正确的长度。假定该字段是一个字符串。如果设置的既不是最小长度也不是最大长度,那么什么都不要做。

publicclassEmployeeextendsActionSupport{@StringLengthFieldValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true,trim=true,minLength="5",maxLength="12")publicStringgetName(){returnname;}}

这个验证器检查一个字段是一个有效的URL。

publicclassEmployeeextendsActionSupport{@UrlValidator(message="Defaultmessage",key="i18n.key",shortCircuit=true)publicStringgetURL(){returnurl;}}

如果你想使用多个相同类型的注解,这些注解必须在@Validations()注解中嵌套。

publicclassEmployeeextendsActionSupport{@Validations(requiredFields={@RequiredFieldValidator(type=ValidatorType.SIMPLE,fieldName="customfield",message="Youmustenteravalueforfield.")},requiredStrings={@RequiredStringValidator(type=ValidatorType.SIMPLE,fieldName="stringisrequired",message="Youmustenteravalueforstring.")})publicStringgetName(){returnname;}}

这个注解可以用于自定义验证器。使用ValidationParameter注解来提供额外的参数。

@CustomValidator(type="customValidatorName",fieldName="myField")

在类型层中,这是类型转换的一个标记注解。Conversion注解必须应用在类型层。

@Conversion()publicclassConversionActionimplementsAction{}

这个注解为类型转换设置为CreateIfNull。CreateIfNull注解必须应用在字段或方法层。

@CreateIfNull(value=true)privateListusers;

这个注解为类型转换设置为Element。Element注解必须应用在字段或方法层。

@Element(value=com.acme.User)privateListuserList;

这个注解为类型转换设置为Key。Key注解必须应用在字段或方法层。

@Key(value=java.lang.Long.class)privateMapuserMap;

这个注解为类型转换设置为KeyProperty。KeyProperty注解必须应用在字段或方法层。

@KeyProperty(value="userName")protectedListusers=null;

这个注解的注解用于类和应用程序范围的转换规则。TypeConversion注解必须应用在属性或方法层。

@TypeConversion(rule=ConversionRule.COLLECTION,converter="java.util.String")publicvoidsetUsers(Listusers){this.users=users;}

Struts2标签

控制标签

Struts2有一组标签使得控制页面执行流非常容易。以下是重要的Struts2控制标签的列表:

该标签实现基本的能够在每种语言中找到的条件流。'If'标签能够单独使用或与'ElseIf'标签一起使用,单个/多个'Else'标签如下所示:

WillNotBeExecuted
WillBeExecuted
WillNotBeExecuted

迭代标签会迭代一个值。一个迭代的值可以是任何java.util.Collection或java.util.Iterator。当迭代一个值时,你可以使用Sort标签来把结果分类或者使用SubSet标签得到列表或数组的子集。

以下例子检索了值栈中当前对象的getDays()方法的值,并用它迭代。标签输出了当前迭代的值。

dayis:

融合标签将两个或多个列表作为参数并把它们融合在一起,如下所示:

附加标签将两个或多个列表作为参数并把它们附加在一起,如下所示:

生成器标签生成基于提供的val属性的迭代器。下面的例子中,生成器标签生成了一个迭代器并用迭代器标签把它输出出来。


数据标签

Struts2的数据标签主要用于操作显示在页面中的数据。以下列出的是重要的数据标签:

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

Tagtoexecutetheaction


Toinvokesspecialmethodinactionclass

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

<--FirstSyntax--><--SecondSyntax--><--ThirdSyntax-->value1value2

这些bean标签实例化符合JavaBeans规范的类。该标签的主体可以包含一组参数元素来在那个类中设置任何设值方法。如果var属性设置在BeanTag中,那么它会把初始化的bean放到栈的上下文中。

这些日期标签允许你以快速简单的方法设置日期格式。你可以指定一个自定义的格式(如"dd/MM/yyyyhh:mm"),你可以生成简单的可读符号(如"in2hours,14minutes"),或者你也可以用你的属性文件中的键'struts.date.format'来后退到预定义的格式。

参数标签用于参数化其他标签。这个标签有以下两个参数。

属性标签用于获取值的属性,如果不存在指定的属性,那么就会默认为栈顶的属性。

TextUtils

push标签用于栈中push值的简单操作。

set标签将一个值赋给指定范围中的变量。当你想要为一个复杂的表达式分配一个变量,每次只需要简单的引用这个变量而不需要引用这个复杂的表达式时,这个标签是非常有用的。可用的范围是应用程序,会话,请求,页面和操作。

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

MrSmith

url标签用于创建URL。

<--Example1--><--Example2--><--Example3-->

表单标签

form标签的列表是StrutsUI标签的子集。这些标签帮助呈现Strutsweb应用程序必需的用户接口,并且能够被划分为三种类别。本章将会带你浏览UI标签的这三种类别。

我们已经在我们的示例中使用过这些标签了,在本章中我们将一带而过。让我们看看带有几个简单的UI标签的简单的视图页面email.jsp:

如果你了解HTML,那么所有的标签都是非常普通的HTML标签,只不过在每个标签和不同的属性中带有一个额外的前缀s:。当我们执行上述程序时,我们会得到如下所示的用户接口,前提是你已经为所有用到的键设置了合适的映射。

正如显示的一样,s:head生成了Struts2应用程序必需的javascript和stylesheet。

接下来,我们有s:div和s:text元素。s:div元素用于呈现HTMLDiv元素。这对不想把HTML和Struts标签混合到一起的用户来说是非常有用的。对于这些人,他们必须选择使用s:div元素来呈现div。

s:text元素用于在屏幕上呈现一个文本。

接下来,我们有类似的s:form标签。s:form标签有一个操作属性,决定了表单提交的位置。因为在表单中有一个文件上传元素,我们必须把enctype设置为multipart。否则,我们可以离开这个空白。

在表单标签的结尾,我们有s:submit标签。这是用于提交表单的。当表单被提交时,所有的表单的值都会提交给s:form标签中指定的操作。

在s:form标签内部,有一个隐藏的属性称为secret。这会呈现在HTML中隐藏的元素。在我们的例子中,"secret"元素有"abracadabra"值。这个元素对终端用户是不可见的且它用于将状态从一个视图传递给另一个视图。

接下来,我们有s:label,s:textfield,s:password和s:textarea标签。这是分别用于呈现标记、输入字段、密码和文本域的。我们已经在"Struts-SendingEmail"例子中的操作中了解了这些标签的使用方法。在这里需要注意的很重要的一点是"key"属性的使用。"key"属性是用于从属性文件中为这些控制提取标记的。在Struts2本地化,国际化章节中,我们已经介绍了这个特征。

然后,我们有s:file标签,用于呈现输入文件上传组件。这个组件允许用户上传文件。在这个例子中,我们使用的是s:file标签的"accept"属性来指定哪个文件类型是允许上传的。

最后我们有s:token标签。token标签生成了一个唯一的token,用于确认一个表单是否被提交了两次。

当呈现表单时,一个隐藏的变量就会被替换成token值。比如,token是"ABC"。当表单被提交时,Struts过滤器检查与会话中存储的token不一致的token。如果相匹配的话,它会把这个token从会话中移除出去。现在,如果表单被意外的重复提交了(或通过刷新或点击了浏览器的后退按钮),表单就会被重提交,其中token为"ABC"。在这种情况下,过滤器会再次检查会话中与这个token不一致的token。但是由于token"ABC"已经从从会话中被删除了,它不会再次匹配,Struts过滤器就会拒绝这个请求。

组UI标签是用于创建单选按钮和复选框的。让我们看一个带有复选框和单选按钮标签的简单的视图页面HelloWorld.jsp:

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>HelloWorld

当我们执行上述程序时,会得到类似如下所示的输出:

现在让我们看看这个例子。在第一个例子中,我们创建的是一个简单的带有标记"Gender"的单选按钮。单选按钮的名字属性是强制性的,所以我们指定了名字为"Gender"。然后我们给这个gender提供了一个列表。列表用值"male"和"female"填充。因此,在输出中,我们得到了带有两个值的单选按钮。

在第二个例子中,我们创建的是复选框列表。这是用来收集用户爱好的。用户可能有多个爱好,因此我们使用的是复选框而不是单选按钮。复选框用列表"sports","Tv"和"Shopping"填充。这把爱好以复选框列表的形式展现出来。

让我们展示Struts提供的选择标签的不同的变量。请看如下所示的带有选择标签的简单的视图页面HelloWorld.jsp:

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>HelloWorld

当我们执行上述程序时,我们会得到类似于如下所示的输出:

现在让我们仔细浏览上述情况

在上述例子中,我们做了一个比较来看顶部选择框是否就是Techical。如果是的话,那么会显示IT和Hardware。我们也需要给顶部框("name='Occupations')和底部框(doubleName='occupations2')设置名称。

Ajax标签

Struts使用DOJO框架实现AJAX标签。首先,在介绍例子之前,你需要将struts2-dojo-plugin-2.2.3.jar添加到你的类路径中。你可以从下载的struts2的lib文件夹中获取这个文件(C:\struts-2.2.3-all\struts-2.2.3\lib\struts2-dojo-plugin-2.2.3.jar)。

在这次实践中,让我们修改HelloWorld.jsp文件,如下所示:

当我们运行上述例子时,会得到如下所示的输出:

现在让我们一步步的看这个例子。

首先需要注意的是用prefixsx添加的新的标签库。这(struts-dojo-tags)是专门为ajax集成创建的标签库。

然后在HTML头信息内部,我们调用sx:head。它会初始化dojo框架并为页面内的所有的AJAX调用做好准备。这一步是很重要的——没有sx:head初始化,你的ajax调用不会起作用。

首先我们得到autocompleter标签。autocompleter标签看起来与下拉选框非常相似。它有三个常用的值red,green和blue。但是它和下拉选框之间的区别是它是自动实现的。也就是说,如果你键入gr,那么它就会用"green"填充。除此之外,该标签与我们之前提到的s:select标签就是非常相似的了。

接下来我们为system.action文件创建一个url标签,该文件在之前的实践中已经创建过了。不一定非得是system.action文件——它可以是你之前创建的任何操作文件。然后我们得到一个div,带有对url的超连接设置以及延迟设置为2秒。当你运行它的时候,"InitialContent"会延迟2秒,然后div的内容会被hello.action执行中的内容取代。

最后我们得到一个带有两个标签的标签面板。标签是带有Tab1和Tab2标记的div。

值得注意的是,在Struts中的AJAX标签集成仍然是一项有待改善的工作,该集成也随着每次发布慢慢的成熟起来。

Struts2集成

Spring集成

Spring是一个流行的web框架,提供了与大量普通web任务的简单集成。所以问题是,当我们已经有了Struts2时,为什么还需要Spring?好吧,Spring不仅仅是一个MVC框架——它还提供了许多在Struts中不可用的东西。例如:对任何框架都有用的依赖注入。在本章中,我们将通过一个简单的示例来展示如何集成Spring和Struts2。

最后,将struts2-spring-plugin-x.y.z.jar添加到你的strutslib目录WEB-INF/lib中。如果你使用的是Eclipse,那么你可能会面临一个异常java.lang.ClassNotFoundException:org.springframework.web.context.ContextLoaderListener。为了修复这个问题,进入Marker标签,然后一个接一个的右键单击类依赖,并且进行快速修复来发布/导出所有的依赖。最后,确保在marker标签下没有可用的依赖冲突。

现在让我们为Struts-Spring集成配置web.xml文件,如下所示:

在这里需要注意的一个重要的点是我们已经配置的监听器。加载spring上下文文件需要ContextLoaderListener。Spring的配置文件称为applicationContext.xml,且它必须要放置在和web.xml文件同级的位置。

让我们创建一个简单的操作类,命名为User.java,带有两个属性——firstName和lastName。

packagecom.tutorialspoint.struts2;publicclassUser{privateStringfirstName;privateStringlastName;publicStringexecute(){return"success";}publicStringgetFirstName(){returnfirstName;}publicvoidsetFirstName(StringfirstName){this.firstName=firstName;}publicStringgetLastName(){returnlastName;}publicvoidsetLastName(StringlastName){this.lastName=lastName;}}

现在让我们创建applicationContext.xmlspring配置文件并实例化User.java类。正如先前提到的一样,这个文件应该在WEB-INF文件夹下:

正如我们在上面看见的一样,我们已经配置了userbean并且已经将值Michael和Jackson注入到bean中。我们还将这个bean命名为"userClass",这样我们就可以在任何地方重用它。接下来,我们在WebContent文件夹中创建User.jsp:

User.jsp文件非常简单。它只用于一个目的——显示用户对象的firstname和lastname的值。最后,让我们将在struts.xml文件中用到的条目放到一起。

需要注意的重要的一点是我们用iduserClass来代表类。这意味着我们使用的是spring来为User类进行依赖注入。

现在我们已经了解了如何将这两个好的框架集成到一起。这包括Struts-Spring集成这一章。

Tiles集成

在本章中,让我们浏览用Struts2集成Tiles框架的步骤。ApacheTiles是模板框架,用于简化web应用程序用户接口开发的。

除了上述文件,我么还需要将下列来自struts2的jar文件复制到你的WEB-INF/lib中。

现在让我们为Struts-Tiles集成配置web.xml文件,如下所示。这里有两个重要的点需要注意。首先,我们需要告诉tiles在哪里可以找到tiles配合文件tiles.xml。在我们的例子中,它位于/WEB-INF文件夹下。接下来,我们需要初始化来自Struts2下载的Tiles监听器。

接下来,让我们在/WEB-INF文件夹下创建tiles.xml,其内容如下所示:

接下来,我们在baseLayout.jsp文件中定义一个基本框架布局。它有五个可重用的/可覆盖的域。即title,banner,menu,body和footer。我们为这个基本布局提供了默认值,然后创建了从默认值中扩展的两个自定义值。tiger布局与基本布局类似,除了它使用tiger.jsp作为它的主体,文本"Tiger"作为标题。类似的,lion布局也与基本布局相似,除了它使用lion.jsp作为它的主体,文本"Lion"作为标题。

让我们看看单独的jsp文件。以下是baseLayout.jsp文件的内容:

这里我们将有tiles属性的HTML页面放到一起。我们将tiles属性放到我们需要的位置。接下来,让我们创建banner.jsp文件,其内容如下所示:

menu.jsp文件有如下所示的行,能够链接到TigerMenu.action和LionMenu.actionstruts操作。

lion.jsp文件有如下所示的内容:

tiger.jsp文件有如下所示的内容:

接下来,让我们创建操作类文件MenuAction.java,其内容如下所示:

packagecom.tutorialspoint.struts2;importcom.opensymphony.xwork2.ActionSupport;publicclassMenuActionextendsActionSupport{publicStringtiger(){return"tiger";}publicStringlion(){return"lion";}}

接下来,如果说请求是为/tigerMenu.action的,那么将用户带到tigertiles页面,如果请求是为/lionMenu.action的,那么将用户带到liontiles页面。

我们通过使用一些常规表达式来实现这点。在我们的操作定义中,我们说匹配模式"*Menu"将由这个操作处理。匹配方法包括在MenuAction类中。也就是,tigerMenu.action会包括tiger()且lionMenu.action会包括lion()。然后我们需要将产生的结果映射到适当的tiles页面中。

同样的,如果你进入lionMenu.action页面,那么你将会看到lion页面,它使用的相同的tiles布局。

Hibernate集成

Hibernate是一个高性能的对象/关系持久性,能够查询在开源GNULesserGeneralPublicLicense(LGPL)下的服务较小,并且是免费下载的。在这一章,我们要学习如何用Hibernate集成实现Struts2。

在本教程中,我将使用“struts2_tutoria”MySQL数据库。我使用用户名“root”、无密码来连接到我计算机中的数据库。首先,你需要运行以下脚本。这个脚本创建一个新表,名为student,并在表中创建几条记录:

CREATETABLEIFNOTEXISTS`student`(`id`int(11)NOTNULLAUTO_INCREMENT,`first_name`varchar(40)NOTNULL,`last_name`varchar(40)NOTNULL,`marks`int(11)NOTNULL,PRIMARYKEY(`id`));--##--Dumpingdatafortable`student`INSERTINTO`student`(`id`,`first_name`,`last_name`,`marks`)VALUES(1,'George','Kane',20);INSERTINTO`student`(`id`,`first_name`,`last_name`,`marks`)VALUES(2,'Melissa','Michael',91);INSERTINTO`student`(`id`,`first_name`,`last_name`,`marks`)VALUES(3,'Jessica','Drake',21);

接下来让我们创建hibernate.cfg.xml文件,它是hibernate的配置文件。

接下来你需要为这个项目创建许多jar文件。附件是所需的JAR文件的完整列表截图:

大多数JAR文件可以作为你的struts发行版的一部分获取。如果你有一个已安装的应用程序服务器,如glassfish,websphere或jboss,那么你就可以从应用程序服务器的lib文件夹中得到大部分剩余的jar文件。如果没有的话,那么你可以分别下载这些文件:

剩余的文件,你应该能够从你的struts2发行版中得到。

现在让我们为hibernate集成创建必需的java类。下面是Student.java的内容:

packagecom.tutorialspoint.hibernate;importjavax.persistence.Column;importjavax.persistence.Entity;importjavax.persistence.GeneratedValue;importjavax.persistence.Id;importjavax.persistence.Table;@Entity@Table(name="student")publicclassStudent{@Id@GeneratedValueprivateintid;@Column(name="last_name")privateStringlastName;@Column(name="first_name")privateStringfirstName;privateintmarks;publicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publicStringgetLastName(){returnlastName;}publicvoidsetLastName(StringlastName){this.lastName=lastName;}publicStringgetFirstName(){returnfirstName;}publicvoidsetFirstName(StringfirstName){this.firstName=firstName;}publicintgetMarks(){returnmarks;}publicvoidsetMarks(intmarks){this.marks=marks;}}

这是一个POJO类,代表了每个Hibernate指定的student表。它有id,firstname和lastname属性,与student表中的列名相对应。下面让我们创建StudentDAO.java文件,如下所示:

packagecom.tutorialspoint.hibernate;importjava.util.ArrayList;importjava.util.List;importorg.hibernate.Session;importorg.hibernate.Transaction;importcom.googlecode.s2hibernate.struts2.plugin.\annotations.SessionTarget;importcom.googlecode.s2hibernate.struts2.plugin.\annotations.TransactionTarget;publicclassStudentDAO{@SessionTargetSessionsession;@TransactionTargetTransactiontransaction;@SuppressWarnings("unchecked")publicListgetStudents(){Liststudents=newArrayList();try{students=session.createQuery("fromStudent").list();}catch(Exceptione){e.printStackTrace();}returnstudents;}publicvoidaddStudent(Studentstudent){session.save(student);}}

StudentDAO类是Student类的数据访问层。它有列出所有students和保存一个新的student记录的方法。

下述文件AddStudentAction.java定义了action类。在这里我们同样有两个action方法-execute()和listStudents()。execute()方法用来添加新的student记录。我们使用dao的save()方法来获取。另一个方法,listStudents()用来列出students。我们使用dao的list方法来得到所有student的列表。

packagecom.tutorialspoint.struts2;importjava.util.ArrayList;importjava.util.List;importcom.opensymphony.xwork2.ActionSupport;importcom.opensymphony.xwork2.ModelDriven;importcom.tutorialspoint.hibernate.Student;importcom.tutorialspoint.hibernate.StudentDAO;publicclassAddStudentActionextendsActionSupportimplementsModelDriven{Studentstudent=newStudent();Liststudents=newArrayList();StudentDAOdao=newStudentDAO();@OverridepublicStudentgetModel(){returnstudent;}publicStringexecute(){dao.addStudent(student);return"success";}publicStringlistStudents(){students=dao.getStudents();return"success";}publicStudentgetStudent(){returnstudent;}publicvoidsetStudent(Studentstudent){this.student=student;}publicListgetStudents(){returnstudents;}publicvoidsetStudents(Liststudents){this.students=students;}}

你会注意到我们正在实现ModelDriven接口。当你的action类正在处理一个具体的模型类(如Student)而不是单个的属性(如firstName,lastName)时,该接口就会被用到。ModelAware接口需要你实现一个方法来返回这个模型。在我们的例子中,我们返回的是“student”对象。

现在让我们创建student.jsp视图文件,其内容如下所示:

<%@pagecontentType="text/html;charset=UTF-8"%><%@taglibprefix="s"uri="/struts-tags"%>HelloWorld


FirstNameLastNameMarks

student.jsp非常简单。在最上面的一节中,我们有一个要提交到"addStudent.action"的表单。它带有firstName,lastName和标记。由于addStudent操作与ModelAware"AddSudentAction"绑定了,那么就会自动创建一个带有firstName,lastName和自动填充标记的studentbean。

在最下面的一节中,我们查看了student列表(请看AddStudentAction.java)。我们遍历该列表,并显示表中的firstname,lastname和标记的值。

让我们使用struts.xml将全部的东西放到一起:

listStudent操作方法在AddStudentAction类中调用listStudents(),并把student.jsp作为视图来使用。

在最上面的一节中,我们得到了一个表单,能够为新的student记录输入值,在最下面的一节中列出了数据库中的students。请继续添加新的student记录并按下提交。每次你按下提交按钮后,屏幕会刷新并显示更新的列表。

Struts2有用的资源

有用的资源

以下资源包含关于Strut2的附加说明。请使用它们来获得更多的对这一主题的深入了解。

THE END
1.Java面试题022. 什么叫过滤器(filter)? 答:就是对servlet请求起到过滤的作用,它在监听器之后,作用在servlet之前。比如编码过滤器,就是经过了该过滤器的请求都会设置成过滤器中指定的编码。过滤器是随web应用启动而启动,只初始化一次,只有当web应用停止或重新部署的时候才销毁。 https://developer.aliyun.com/article/925971
2.拦截器过滤器监听器各有什么作用?然后在编写自己的拦截器,web.xml文件中也需要配置 最后在需要拦截的方法上标上自定义注解,表示需要拦截这个方法。 2.未登录或者非vip的话,可以试看10分钟怎么解决 ? 将视频分段,前十分钟是一个请求,之后再次请求,拦截后边这个请求 3.拦截器与过滤器的区别 : (1)拦截器是基于java的反射机制的,而过滤器是基于函数回...http://lesson.jnshu.com/l/subjectContent/204/?id=&lobtn=2
3.JAVAWEB面试题汇总<filter-mapping><filter-name></filter-name> <url-pattern></url-pattern >(<servlet-name></servlet-name>)</filter-mapping > 如果有多个过滤器优先执行url-pattern,再执行servlet-name;如果有多个url-pattern按web.xml布署顺序执行。 60. 你在项目中的角色是什么,你是如何进行工作的? http://www.360doc.com/content/14/0603/18/16924560_383345578.shtml
1.RequestInterceptor配置执行顺序和filter执行顺序filter顾名思义就是过滤器,大家都知道配置过滤器是为了对一个请求进行预处理,然后交给servlet,filter再对响应后处理。filter就像漏斗,对能通过的放行,拦截不能通过的。那么如果有两个过滤器呢?执行顺序是怎么的? 百度百科上对filter的功能描述:它使用户可以改变一个request和修改一个response. Filter 不是一个servlet...https://blog.51cto.com/u_16099212/12627429
2.Javaweb开发核心知识之Filter过滤器文章浏览阅读267次,点赞3次,收藏4次。简介:讲解JavaWeb核?心知识之过滤器?Filter什么是过滤器?:(检验是否符合要求,或者 对内容做?二次处理理,设置编码响应等)Servlet?面的过滤器?作?Filter的生命周期。https://blog.csdn.net/weixin_67996964/article/details/143919983
3.django过滤器和自定义过滤器三、自定义过滤器 实现步骤 1、在子应用下创建python package命名为templatetag(固定的)2、在templatetag下创建python文件,命名myfilter.py(随意设置的)3、myfilter文件中增加过滤器逻辑处理函数4、模板页面顶部加载myfilter文件,{% load filter %}5、在模板页面应用自定义过滤器 举例:myfilter.py文件 from django....https://www.ctyun.cn/zhishi/p-424498
4.过滤器的使用及其实现原理-chain.doFilter(request,response);本行代码的作用: -执行下一个过滤器,如果下面没有过滤器,执行最终的Servlet。 -注:Filter的优先级自然高于Servlet。 -/a.do对应Filter,也对应Servlet。所以在执行Servlet之前,必须先执行Filter。 4.2Filter的配置路径:57 ...https://www.tulingxueyuan.cn/tlzx/jsp/3234.html
5.SpringBoot自定义过滤器的两种方式及过滤器执行顺序如下自定义过滤器 ReqResFilter 必须实现 javax.servlet.Filter。然后添加注解 @WebFilter(javax.servlet.annotation.WebFilter),urlPatterns 过滤器要过滤的URL规则配置,filterName 过滤器的名称。https://cloud.tencent.com/developer/article/1513212
6.JavaFilter过滤器的使用教程javaFilter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能https://www.jb51.net/article/272584.htm
7.2023前端面试题及答案整理(Vue)江阴雨辰互联destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 Vue 的父组件和子组件生命周期钩子执行顺序是什么 加载渲染过程 父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted ...https://www.yc00.com/news/1702142827a1180229.html
8.SpringCloud最全面试题整理,全是干货Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等, 在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。 核心逻辑:路由转发+执行过滤链 47、Spring Cloud GateWay网关配置的两种方式? yaml配置 server: port: 9527 spring: ...https://www.nowcoder.com/discuss/1044144
9.常见vue面试题20190614(版本在不断更新,以下的区别有可能不是很正确。我工作中只用到vue,对angular和react不怎么熟) 1.与AngularJS的区别 相同点: 都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。 不同点: ...https://www.jianshu.com/p/74e2af5771be
10.HXD3B型电力机车在车体顶层由()个进气间组成,使得通过过滤器的进...刷刷题APP(shuashuati.com)是专业的大学生刷题搜题拍题答疑工具,刷刷题提供HXD3B型电力机车在车体顶层由()个进气间组成,使得通过过滤器的进风量尽量均匀。A.一B.二C.三D.四的答案解析,刷刷题为用户提供专业的考试题库练习。一分钟将考试题Word文档/Excel文档/PDF文档转https://www.shuashuati.com/ti/298ac2a6e9b74803a65e027967f5e96a.html?fm=bd95c9675c4c31e32bf63bcdb63d0d3705