Struts教程

本教程详述了 Struts2 的安装配置、基本使用流程、表单处理、验证机制及文件上传等功能,并深入介绍了拦截器的工作原理及其自定义方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Struts2教程1:第一个 Struts2程序
在本系列教程中我们将学习到 Struts2的各种技术。在本教程中使用的工具和程序库的版本

如下:开发工具:MyEclipse6Web服务器:Tomcat6Struts版本:Struts2.0.11.1J DK版本:J DK1.5.0_12J2EE版本:Java EE5.0

在本系列教程中Web工程的上下文路径都是stru ts2,如果在Web根目录有一个index .jsp文件,则访问路径如下:

http://localhost:8080/struts2/index.jsp

由于M yE clipse6目前并不支持S truts2, 所 以我们需要到stru ts.apa che.org 去下载S truts 2安装包。要想正常使用Struts2,至少需要如下五个包(可能会因为Struts2的版本不同,包名略有差异,但包名的前半部是一样的) 。

struts2-core-2.0.11.1.jarxwork-2.0.4.jarcommons-logging-1.0.4.jarfreemarker-2.3.8.jarognl-2.6.11.jar

Struts2 虽然在大版本号上是第二个版本,但基本上在配置和使用上已经完全颠覆了Struts1.x的方式(当然,Struts2仍然是基于MVC模式的,也是动作驱动的,可能这是唯一没变的东西) 。Struts2实际上是在Webwork基础上构建起来的MVC框架。我们从Struts2的源代码中可以看到,有很多都是直接使用的xw ork(Webw ork的核心技术)的包。既然从技术上来说Struts2是全新的框架,那么就让我们来学习一下这个新的框架的使用方法。

如果大家使用过Struts1 .x,应该对建立基于Struts1 .x的Web程序的基本步骤非常清楚。让我们先来回顾一下建立基于Struts1 .x的Web程序的基本步骤。

1. 安装Struts。由于Struts的入口点是ActionServlet,所以得在web.xml中配置一下这个Servlet。

2. 编写Action类(一般从org.apache.struts.action.Action类继承) 。
3. 编写ActionForm类(一般从org.apache.struts.action.ActionForm类继承) ,这一步

不是必须的,如果要接收客户端提交的数据,需要执行这一步。
  1. 在stru ts-con fig.x m l文件中配置A ction和A ctionF orm。

  2. 如果要采集用户录入的数据,一般需要编写若干JSP页面,并通过这些JSP页面中

的form将数据提交给Action。

下面我们就按着编写struts1.x程序的这五步和struts2.x程序的编写过程一一对应,看看它们-1-

谁更“酷”。 下面我们来编写一个基于Struts2的Web程序。 这个程序的功能是让用户录入两个整数,并提交给一个Struts Action,并计算这两个数的代数和,如果代码和为非负数,则跳转到positive.jsp页面,否则跳转到negative.jsp页面。

【第1步】 安装Struts2

这一步对于Struts1 .x和Struts2 都是必须的, 只 是安装的方法不同。Struts1 的入口点是一个Servlet, 而Struts2 的入口点是一个过滤器(Filter)。 因此,Struts2 要按过滤器的方式配置。下面是在web.xml中配置Struts2的代码:

<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>

【第2步】 编写Action类
这一步和
Struts1.x也必须进行。只是

Struts1.x中的动作类必须从Struts2.x的动作类需要从com.opensymphony.xwork2.ActionSupport类继承。 下面是计算两个整

数代码和的A cti on类,代码如下:packageaction;

importcom.opensymphony.xwork2.ActionSupport;

public class FirstActionex tendsActionSupport{

privateintoperand1;privateintoperand2;

publicStringexecute()throwsException{

if (getSum()>= 0) // 如果代码数和是非负整数,跳到positive.jsp页面{

return"positive";}

else // 如果代码数和是负整数,跳到negative.jsp页面{

Action类中继承,而

-2-

return"negative";}

}

public int getOperand1(){

returnoperand1;}

publicvoidsetOperand1(intoperand1){

System.out.println(operand1);this.operand1= operand1;

}

public int getOperand2(){

returnoperand2;}

publicvoidsetOperand2(intoperand2){

System.out.println(operand2);

this.operand2= operand2;}

publicint getSum(){

returnoperand1+ operand2; // 计算两个整数的代码数和}

}

从上面的代码可以看出, 动作类的一个特征就是要覆盖execute方法,只是Struts2的execute方法没有参数了, 而Struts1.x的execute方法有四个参数。 而且execute方法的返回值也不同的。Struts2只返回一个String,用于表述执行结果(就是一个标志) 。上面代码的其他部分将在下面讲解。

【第3步】 编写ActionForm类

在本例中当然需要使用ActionForm了。在Struts1.x中, 必须要单独建立一个ActionForm类(或是定义一个动作Form) ,而在Struts2中ActionForm和Action已经二合一了。从第二步的代码可以看出, 后面的部分就是应该写在ActionForm类中的内容。 所以在第2步, 本例的ActionForm类已经编写完成(就是Action类的后半部分)

【第4步】 配置Action类

-3-

这一步 struts1.x和struts2.x都是必须的,只是在 struts1.x中的配置文件一般叫 struts-config.xml(当然也可以是其他的文件名) ,而且一般放到WEB-INF目录中。而在struts2.x中的配置文件一般为struts.xml,放到WEB-INF"classes目录中。下面是在struts.xml中配置动作类的代码:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><packagename="struts2"namespace="/mystruts"

extends="struts-default">
<action name="sum"class="action.FirstAction">

<result name="positiv e">/positiv e.jsp</result>

<result name="negativ e">/negativ e.jsp</result></action>

</package></struts>

在<struts> 标签中可以有多个<packag e>, 第一个<packag e>可以指定一个Servlet 访问路径(不包括动作名) ,如“/mystruts”。extends属性继承一个默认的配置文件“struts-default”,一般都继承于它,大家可以先不去管它。<action>标签中的name属性表示动作名,class表

示动作类名。

<result>标签的name实际上就是execute方法返回的字符串, 如果返回的是“positive”,就跳转到 positive.jsp页面,如果是 neg ative”,就跳转到 neg ative.jsp页面。在 <struts> 中可以有多个<packag e>, 在<packag e>中可以有多个<action> 。 我们可以用如下的U RL来访问这个动作:

http ://localho st:8080 /struts 2/m ystruts/sum .action
注:Struts1 .x的动作一般都以.do 结尾,而Struts2是以.actio n结尾。【第5步】 编写用户录入接口(JSP页面)
1. 主界面( sum .js p)在Web根目录建立一个sum.jsp,代码如下:

<%@ taglib prefix="s"uri="/struts-tags"%>

<html><head>

<title>输入 操作数</title></head>

<%@

page

language="java"import="java.util.*"

pageEncoding="GBK"%>

-4-

<body>

求代 数和

<br/><s:formaction="mystruts/sum.action">

<s:textfieldname="operand1"label=" 操作数1"/><s:textfieldname="operand2" label=" 操作数2" /><s:submitvalue="代数和" />

</s:form></body>

</html>

在sum .jsp中使用了S truts2 带的tag。 在S truts2中已经将S truts1 .x 的好几个标签库都统一了,在Struts2中只有一个标签库/stru ts-ta gs。这里面包含了所有的Struts2标签。但使用Struts2的标签大家要注意一下。 在<s:form>中最好都使用Struts2 标签, 尽 量不要用H TML或普通文本,大家可以将sum.jsp的代码改为如下的形式,看看会出现什么效果:

... ...

求代数和

<br/>
<s:form action="mystruts/sum.action" >

操作数1:<s:textfield name="operand1" /><br/>操作数2:<s:textfield name="operand1" /><br/>

<s:submit value="代数和" /></s:form>

... ...
提示一下,在<s:form>中Struts2 使用<table>定位。

2. positive.jsp

<%@ taglibprefix="s"uri="/struts-tags"%>

<html><head>

<title>显示 代数和</title></head>

<body>代数和为非负整数<h1><s:propertyvalue="sum"/></h1>

</body></html>

<%@

page

language="java"import="java.util.*"

pageEncoding="GBK%" >

3. negative.jsp

-5-

<%

@

page

language="java"import="java.util.*"

pageEncoding="GBK%" >

<%@ taglibprefix="s"uri="/struts-tags"%>

<html><head>

<title>显示 代数和</title></head>

<body>代数和为负整数<h1><s:propertyvalue="sum"/></h1>

</body></html>

这两个jsp页面的实现代码基本一样, 只 使用了一个<s:pro perty> 标签来显示Action类中的sum属性值。<s:pro perty> 标签是从re quest对象中获得了一个对象中得到的sum 属性,如我们可以使用如下的代码来代替<s:property value=”sum”/>:

<%com.opensymphony.xwork2.util.OgnlValueStackovs =

(com .ope nsym phony.x w ork2.util.Ogn lValu eStack)re quest. getAttribu te("stru ts.valu eStack");

out.println(ovs.findString("sum"));%>

启动T o m cat后,在I E中输入如下的U RL来测试这个例子:http://localhost:8080/struts2/sum.jsp

下一篇:Struts2教程2:处理一个form多个submit

Struts2教程2:处理一个 form多个submit

在很多Web应用中,为了完成不同的工作,一个HTML form标签中可能有两个或多个submit按钮,如下面的代码所示:
<!--[if !supportLineBreakNewLine]-->

<inputtype="submit"value="保存" />

<html action=" " method="post">

-6-

<inputtype="submit"value="打印" /></html>

由于在<form>中的多个提交按钮都向一个action提交,使用Struts2 Action的execute方法就无法判断用户点击了哪一个提交按钮。 如果大家使用过Struts1.x就会知道在Struts1.2.9之前的版本需要使用一个 LookupDispatchAction动作来处理含有多个 submit的form 。但使用LookupDispatchAction动作需要访问属性文件, 还需要映射, 比较麻烦。S从truts1.2.9开始,加入了一个EventDispatchAction动作。这个类可以通过java反射来调用通过request参数指定的动作 (实际上只是判断某个请求参数是不存在,如果存在, 就调用在action 类中和这个参数同名的方法) 。使用EventDispatchAction必须将submit的name属性指定不同的值以区分每个submit。而在Struts2中将更容易实现这个功能。

当然,我们也可以模拟EventDispatchAction的方法通过request获得和处理参数信息。但这样比较麻烦。 在Struts2中提供了另外一种方法, 使 得无需要配置可以在同一个action 类中执行不同的方法(默认执行的是execute方法) 。使用这种方式也需要通过请求参来来指定执要行的动作。请求参数名的格式为

action!method.action注:由于Struts2只需要参数名,因此,参数值是什么都可以。下面我就给出一个实例程序来演示如何处理有多个submit的form:【第1步】实现主页面(more_submit.jsp)

<%@
<%@ %><html>

<head>
<title>MyJSP 'hello.jsp'startingpage</title>

</head>

<body><s:formaction="submit.action">

<s:textfieldname="msg"label="输入内容"/><s:submitname="save"value="保存" align="left"method="save"/><s:submitname="print"value="打印" align="left"method="print"/>

</s:form></body>

</html>
在more_submit.jsp中有两个submit:保存和打印。 其中分别通过method属性指定了要调用的方法:save和print。因此,在Action类中必须要有save和print方法。

【第2步】实现Action类(MoreSubmitAction)packageaction;

page

language="java"import="java.util.*"

pageEncoding="GBK%" >

taglib

prefix="s"

uri="/struts-tags"

-7-

importjavax.servlet.http.*;

importcom.opensymphony.xwork2.ActionSupport;importorg.apache.struts2.interceptor.*;

public class MoreSubmitActionextendsActionSupportimplementsServletRequestAware{

privateStringmsg;privatejavax.servlet.http.HttpServletRequesret quest;//获得HttpServletReques对t象
public v oid setServ letRequest(HttpServ letRequesret quest){

this.request= request;}

// 处理savesubmit按钮的动作public String sav e()throws Exception{

request.setAttribute("result"",成功保存[" + msg + "]");

return"save";}

// 处理print submit按钮的动作
public String print() throws Exception{

request.setAttribute("result"",成功打印[" + msg + "]");

return"print";}

public String getMsg(){

returnmsg;}

public voidsetMsg(Stringmsg){

this.msg= msg;}

}

上面的代码需要注意如下两点:

save和print方法必须存在,否则会抛出java.lang.NoSuchMethodException异常。

Struts2 Action动作中的方法和Struts1.x Action的execute不同, 只使用Struts2 Action动作的execute方法无法访问request对象,因此,Struts2 Action类需要实现一个Struts2自带的拦截器来获得request对象,拦截器如下:

-8-

org.apache.struts2.interceptor. ServletRequestAware【第3步】配置Struts2 Action
struts.xml的代码如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStruts Configuration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><packagename="demo"extends="struts-default">

<actionname="submit" class="action.MoreSubmitAction"><resultname="save">

/result.jsp</result>

<resultname="print">/result.jsp

</result></action>

</package></struts>

【第4步】编写结果页(result.jsp)

<html><head>

<title>提交 结果</title></head>
<body>

<h1>${result}</h1></body>

</html>在result.jsp中将在save和print方法中写到request属性中的执行结果信息取出来,并输出到客户端。

启动T o m cat后,在I E中执行如下的U RL来测试程序:http://localhost:8080/moresubmit/more_submit.jsp

大家也可以直接使用如下的URL来调用save和print方法:

调用save方法:http://localhost:8080/moresubmit/submit!save.action调用prin t方法:http ://localho st:8080 /m oresubm it/subm it!print. action

<%@

page

pageEncoding="GBK%" >

-9-

源代码:http://www.itpub.net/attachment.php?aid=520773

Struts2教程3:struts.xml 常用配置解析

在本文中将详细讲述struts.xml文件的常用配置及注意事项。

1. 使用<include>标签重用配置文件

在Struts2中提供了一个默认的 struts.xml文件,但如果package、action、interceptors等配置比较多时,都放到一个 struts.xml文件不太容易维护。因此,就需要将 struts.xml文件分成多个配置文件,然后在 struts.xml文件中使用<include>标签引用这些配置文件。这样做的优点如下:

结构更清晰,更容易维护配置信息。
配置文件可以复用。如果在多个
Web程序中都使用类似或相同的配置文件,那么可以使用

<include>标签来引用这些配置文件,这样可以减少工作量。假设有一个配置文件,文件名为newstruts.xml,代码如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStruts Configuration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><packagename="demo"extends="struts-default">

<actionname="submit" class="action.MoreSubmitAction"><resultname="save">

/result.jsp</result>

<resultname="print">/result.jsp

</result></action>

</package></struts>

则struts.xml引用newstruts.xml文件的代码如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStruts Configuration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><includefile="newstruts.xml"/>

- 10 -

<packagename="test"extends="struts-default">

</package></struts>

大家要注意一下,用 <include>引用的 xml文件也必须是完成的 struts2的配置。实际上<include> 在引用时是单独解析的x m l文件, 而 不是将被引用的文件插入到stru ts.x m l文件中。

2. acti on的别名

在默认情况下,Struts2会调用动作类的execute方法。但有些时候,我们需要在一个动作类中处理不同的动作。 也 就是用户请求不同的动作时, 执 行动作类中的不同的方法。 为 了达到这个目的,可以在<acti on>标签中通过method方法指定要指行的动作类的方法名,并且需要为不同的动作起不同的名子(也称为别名) 。如下面代码所示:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><packagename="demo"extends="struts-default">

<action name="test" class="action.MyAction">

</action>
<action name="my" class="action.MyAction"method="my">

</action></package>

</struts>上面代码的两个动作的class属性都指向同一个类,name为这个类起了两个动作别名:test和my。在动作my中,使用了method属性指定要要运行的方法名为my。

在MyActi on类中必须要有my方法,代码如下:packageaction;

importcom.opensymphony.xwork2.ActionSupport;

public class MyActionextendsActionSupport{

public String execute()throws Exception{

// 处理test动作的代码

- 11 -

}publicStringmy()throwsException{

// 处理my动作的代码}

}

除了在struts.xml中配置别名,还可以通过请求参数来描述指定动作(并不需要在struts.xml中配置) 。请求参数的格式如下:

http ://localho st:8080 /contex tPath/actionN am e!m ethod .action关于通过请求指定动作的详细内容,请参阅笔者写的 《Struts2 教程 2:处理一个 form多个

submit》。

3. 为acti on指定参数

在struts2中还可以为action指定一个或多个参数。大家还记着 struts1.x是如何设置的action参数不? 在struts1.x中可以使用<action>标签的parameter属性为其指定一个action参数,如果要指定多个,就只能通过逗号(,)或其他的分隔符将不同的参数隔开。而在struts2中可以通过<param>标签指定任意多个参数。代码如下:

<actionname="submit" class="action.MyAction">

<paramname="param1">value1</param><paramname="param2">value2</param>

<result name="sav e">/result.jsp

</result></action>

当然, 在acti on中读这些参数也非常简单, 只 需要象获取请求参数一样在acti on类中定义相应的setter方法即可(一般不用定义getter方法) 。如下面的代码将读取 param1和param2参数的值:
packageaction;

importcom.opensymphony.xwork2.ActionSupport;

public class MyActionextendsActionSupport{

privateStringparam1;privateStringparam2;

- 12 -

public String execute()throws Exception{

System.out.println(param1+ param2);}

public v oid setParam1(Stringparam1){

this.param1= param1;}

public v oid setParam2(Stringparam2){

this.param2= param2;}

}当struts2在调用execute之前,param1和param2的值就已经是相应参数的值了,因此,在

execute方法中可以直接使用param1和param2。4. 选择r esul t类型

在默认时,<result>标签的type属性值是“dispatcher”(实际上就是转发,forward) 。开发人员可以根据自己的需要指定不同的类型,如re direct 、stre am等。如下面代码所示:

<result name="save" type="redirect">/result.jsp

</result>

这此result-type可以在 struts2-core-2.0.11.1.jar包或struts2源代码中的 struts-default.xml文件中找到, 在 这个文件中找到<result- types> 标签, 所 有的re sult-type 都在里面定义了。 代码如下:

<result-types>

<result-typename="chain"class="com.opensymphony.xwork2.ActionChainResult"/>

<result-type name="dispatcher"class="org.apache.struts2.dispatcher.Serv letDispatcherResudlet"fault="true"/>

<result-type name="freemarker"class="org.apache.struts2.views.freemarker.FreemarkerResult"/>

<result-typename="httpheader"class="org.apache.struts2.dispatcher.HttpHeaderResult"/>

<result-type name="redirect"class="org.apache.struts2.dispatcher.ServletRedirectResult"/>

<result-type name="redirectAction"

- 13 -

class="org.apache.struts2.dispatcher.Serv letActionRedirectResult"/><result-typename="stream"class="org.apache.struts2.dispatcher.StreamResult"/><result-typename="velocity"class="org.apache.struts2.dispatcher.VelocityResult"/><result-typename="xslt"class="org.apache.struts2.views.xslt.XSLTResult"/><result-typename="plainText"class="org.apache.struts2.dispatcher.PlainTextResu/>lt"<!-- Deprecatedname form scheduledforremovalin Struts 2.1.0.ThecamelCaseversions

are preferred.See ww-1707-->
<result-type name="redirect-action"

class="org.apache.struts2.dispatcher.Serv letActionRedirectResult"/><result-typename="plaintext"class="org.apache.struts2.dispatcher.PlainTextResu/>lt"

</result-types>

5. 全局result有很多时候一个<result>初很多<action>使用,这时可以使用<global-results>标签来定义全

局的<result> ,代码如下:

<struts>

<packagename="demo"extends="struts-default"><global-results>

<resultname="print">/result.jsp</result></global-results>

<actionname="submit"class="action.MoreSubmitAction">

</action>
<actionname="my"class="action.MoreSubmitActionm" ethod="my">

</action></package>

</struts>

如果<acti on>中没有相应的<resul t>,Struts2就会使用全局的<resul t>。

Struts2教程4:使用validate方法验证数据

在Struts2中最简单的验证数据的方法是使用validate。 我们从ActionSupport类的源代码中可以看到,Act ion Su pport 类实现了一个V al i dat eabl e接口。 这个接口只有一个v alidate方法。 如 果Action类实现了这个接口,Stru ts2在调用ex ecu te方法之前首先会调用这个方法,我们可以在validate方法中验证,如果发生错误,可以根据错误的level选择字段级错误, 还是动作级错误。 并且可使a用ddFieldError或addA ct ion Er r or加入相应的错误信息, 如果存在Act ion 或Fi el d错误,St r u t s2会返回“input”(这个并不用开发人员写, 由Struts2自动返回) , 如果返回了“input”,

- 14 -

Stru ts2就不会再调用ex ecu te方法了。 如 果不存在错误信息,Stru ts2在最后会调用ex ecu te方法。

这两个add方法和Action Errors类中的add方法类似,只是add方法的错误信息需要一个 ActionMessage对象,比较麻烦。除了加入错误信息外,还可以使用
addA ct ion M essage方法加入成功提交后的信息。当提交成功后,可以显示这些
信息。

以上三个add方法都在ValidationAware接口中定义, 并且在ActionSupport类中有一个默认的实现。其实,在ActionSupport类中的实现实际上是调用了ValidationAwareSuppor中t的相应的方法,也就是这三个add方法是在ValidationAwareSuppor类t中实现的,代码如下:

p r i v a t e f i n a l V a l i d a t i o n A w a r e S u p p o rvt a l i d a t i o n A w a r e= n e w V a l i d a t i o n A w a r e S u p p o r t ( ) ;

public v oidaddActionError(StringanErrorMessage)
{ v alidationAware.addActionError(anErrorMessage);}
public v oidaddActionMessage(StringaMessage)
{

v alidationAware.addActionMessage(aMessage);}

public v oidaddFieldError(StringfieldName,String errorMessage){

validationAware.addFieldError(fieldName,rrorMessage);}

下面我们来实现一个简单的验证程序,来体验一个validate方法的使用。先来在Web根目录建立一个主页面(validate.jsp) ,代码如下:

<%@ page language="java"import="java.util.*"pageEncoding="GBK"%>

<%@ taglib prefix="s"uri="/struts-tags"%><html>

<head>
<title>验证
数据</title>

</head>

<body>
<s:actionerror/>
<s:actionmessage/><s:formaction="validate.action" theme="simple">

输入内容:<s:textfieldname="msg"/><s:fielderrorkey="msg.hello"/><br/>

<s:submit/></s:form>

</body>

- 15 -

</html>
在上面的代码中,使用了 Struts2 tag : <s:actionerror> <s:fielderror> <s:actionm essag e>,分别用来显示动作错误信息, 字段错误信息,和动作信息。 如果信息为空,则不显示。

现在我们来实现一个动作类,代码如下:

packageaction;

importjavax.servlet.http.*;

importcom.opensymphony.xwork2.ActionSupport;importorg.apache.struts2.interceptor.*;

public class ValidateActionextendsActionSupport{

privateStringmsg;public String execute(){

System.out.println(SUCCESS);

returnSUCCESS;}

public v oid v alidate(){

if(!msg.equalsIgnoreCase("hello")){

System.out.println(INPUT);
thi s. addFieldError("m sg. hello"",必须
输入hello! ");this.addActionError(处"理动作失败!");

}else{

this.addActionMessage(提"交成功");}

}
public String getMsg(){

returnmsg;}

public voidsetMsg(Stringmsg){

this.msg= msg;}

}

- 16 -

大家从上面的代码可以看出, Field错误需要一个 key(一般用来表示是哪一个属性出的错误) ,而Action错误和Action消息只要提供一个信息字符串就可以了。

最后来配置一下这个Action,代码如下:<packagename="demo"extends="struts-default">

<actionname="validate"class="action.ValidateAction"><resultname="success">/error/validate.jsp</result><resultname="input">/error/validate.jsp</result>

</action></package>

假设应用程序的上下文路径为dem o,则可通过如下的U RL来测试程序:

http://localhost:8080/demo/validate.jsp

我们还可以使用Va lidationAw are接口的其他方法 ( 由Va lidationAw areSuppo rt类实现) 获得或设置字段错误信息、动作错误信息以及动作消息。如 hasActionErrors方法判断是否存在动作层的错误,getFieldErrors获得字段错误信息(一个Map对象) 。下面是ValidationAware接口提供的所有的方法:

packagecom.opensymphony.xwork2;

importjava.util.Collection;importjava.util.Map;

public interfaceValidationAware{

v oid setActionErrors(CollectionerrorMessages);Collection getActionErrors();

v oid setActionMessages(Collectionmessages);Collection getActionMessages();voidsetFieldErrors(MaperrorMap);
Map getFieldErrors();

v oid addActionError(StringanErrorMessage);
v oid addActionMessage(StringaMessage);voidaddFieldError(StringfieldName,StringerrorMessage);boolean hasActionErrors();
boolean hasActionMessages();
boolean hasErrors();
boolean hasFieldErrors();

}

- 17 -

Struts2教程5:使用Validation框架验证数据

在《Struts2教程4:使用validate 方法验证数据》中曾讲到使用validate 方法来验证客户端提交的数据,但如果使用validate方法就会将验证代码和正常的逻辑代码混在一起,但这样做并不利于代码维护,而且也很难将过些代码用于其他程序的验证。在 Struts2中为我们提供了一个Validation框架,这个框架和Struts1.x提供的Validation框架类似,也是通过X ML文件进行配置。

一、服务端验证

下面将给出一个例子来演示如何使用Struts2的validation框架来进行服务端验证。我们可以按着如下四步来编写这个程序:

【第1步】建立Action类(N ewValidateAction .java)

packageaction;

importcom.opensymphony.xwork2.ActionSupport;

public class NewValidateActionextendsActionSupport{

privateStringmsg; // 必须输入privateintage; // 在13和20之间public String getMsg()
{

returnmsg;}

public voidsetMsg(Stringmsg){

this.msg= msg;}

public int getAg e(){

return age;}

public v oid setAg e(intage){

this.age= age;}

}

下面我们来验证msg和age属性。【第2步】配置Action类,struts.xml的代码如下:

- 18 -

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><packagename="demo"extends="struts-default"namespace="/test">

<actionname="new_validate"class="action.NewValidateAction"><resultname="input">/validate_form.jsp</result><resultname="success">/validate_form.jsp</result>

</action></package>

</struts>

【第3步】编写验证规则配置文件

这是一个基于 XML的配置文件,和 struts1.x中的validator框架的验证规则配置文件类似。但一般放到和要验证的.class文件在同一目录下,而且配置文件名要使用如下两个规则中的一个来命名:

<ActionClassName>-validation.xml<ActionClassName>-<ActionAliasName>-validation.xml

其中<ActionAliasName>就是struts.xml中<ation>的name属性值。 在本例中我们使用第一种命名规则,所以文件名是NewValidateAction-validation.xm。l文件的内容如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEvalidatorsPUBLIC"-//OpenSymphonyGroup//XWorkValidator1.0.2//EN""http://www .opensymphony.com/xwork/xwork-v alidator-1.0.2.dtd">
<v alidators>

<field name="msg">
<field-v alidatortype="requiredstring">

<message>请输 入信息</message></field-v alidator>

</field>
<field name="age">

<field-v alidatortype="int"><paramname="min">13</param><paramname="max">20</param><message>

必须在 13至20之间</message>

</field-v alidator></field>

</v alidators>

- 19 -

这个文件使用了两个规则:requiredstring(必须输入)和int(确定整型范围) 。关于其他更详细的验证规则, 请读者访问http ://stru ts.apach e.org /2.0. 1 1.1/d ocs/validatio n.htm l来查看。

【第4步】编写数据录入JSP页。在Web根目录中建立一个validate _form .jsp文件,代码如下:

<%@ page language="java"import="java.util.*"pageEncoding="GBK"%>

<%@ taglib prefix="s"uri="/struts-tags"%>
<link rel="stylesheet"type="text/css"href="<s:urlv alue="/styles/styles.css"/>"><html>

<head>
<title>验证
数据</title>

</head><body>

<s:form action="new_v alidate"namespace="/test"><s:textfieldname="msg"label="姓名" /><s:textfieldname="age"label="年龄"/><s:submit/>

</s:form></body>

</html>
大家要注意一下,如果在 struts.xml的<package>标签中指定 namespace属性,需要在<s:form >中也将nam espace 和action分开写, 如 上面代码所示。 不 能将其连在一起,Struts 2需要分开的action和namespace。如下面的代码是错误的:

<s:form action="/test/new_validate" >... ...

</s:form>在上面的程序中还使用了一个styles.css来定制错误信息的风格。代码如下:.label {font-style:italic; }
.errorLabel {font-style:italic; color:red; }
.errorMessage {font-weight:bold; color:red; }

需要在Web根目录中建立一个styles目录,并将styles.css假设Web工程的上下文路径是validation,可以使用如下的URL来测试这个程序:

http://localhost:8080/validation/validate_form.jsp

显示结果如图1所示。

- 20 -

图1

二、客户端验证
在Struts2中实现客户端验证非常简单,只需要在
<s:form>中加入一个 validate属性,值为

true。如<s:form validate="true" ... > ... </form>即可。三、验证嵌套属性

有一类特殊的属性, 即这个属性的类型是另外一个JavaB ean, 如有一个U ser类, 代码如下:

packagedata;

public class User{

privateStringname;priv ate int age;
public String getName(){

return name;}

public v oid setName(Stringname){

this.name= name;}

public int getAg e(){

return age;}

public v oid setAg e(intage)

- 21 -

{
this.age= age;

}}

在NewV al i dateActi on类中加一个user 属性,代码如下:

packageaction;

importcom.opensymphony.xwork2.ActionSupport;importdata.User;

public class NewValidateActionextendsActionSupport{

privateStringmsg;priv ate int age;privateUseruser;public String getMsg(){

returnmsg;}

public voidsetMsg(Stringmsg){

this.msg= msg;}

public int getAg e(){

return age;}

public v oid setAg e(intage){

this.age= age;}

public User getUser(){

returnuser;}

public v oid setUser(Useruser){

this.user= user;}

}

- 22 -

如果要验证N ewValidateAction中的user属性,可以使用visitor验证器。操作过程如下:首先在NewValidateAction-validation.xm中l加入一个<field>标签,代码如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEvalidatorsPUBLIC"-//OpenSymphonyGroup//XWorkValidator1.0.2//EN""http://www .opensymphony.com/xwork/xwork-v alidator-1.0.2.dtd">
<v alidators>

<field name="user">
<field-v alidatortype="v isitor">

<paramname="context">abc</param><paramname="appendPrefix">true</param><message>User:</message>

</field-v alidator></field>

</v alidators>

其中context参数将作为验证User类属性的文件名的一部分, 如user属性返回一个User对象,那么用于验证User对象属性的文件名为 User-abc-validation.xml。这个文件要和 User.class文件在同一个目录中。app endPrefix 表示是否在字段里加user, 如果为tru e,Struts2 就会使用user . nam e在form 提交的数据中查找要验证的数据。 这 个属性的默认值是tru e。 如 果出错,Struts2会将<message>标签中的信息加到 User-abc-validation.xml文件中的相应错误信息前面。

U ser-abc-vali dati on.xml文件的内容如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEvalidatorsPUBLIC"-//OpenSymphonyGroup//XWorkValidator1.0.2//EN""http://www .opensymphony.com/xwork/xwork-v alidator-1.0.2.dtd">

<v alidators>
<field name="name">

<field-v alidatortype="requiredstring">
< mes sage>请输
入name< /m ess age>

</field-v alidator></field>

<field name="age">
<field-v alidatortype="int">

<paramname="min">5</param><paramname="max">20</param><message>

必须在 5至20之间</message>

</field-v alidator>

- 23 -

</field></v alidators>

下面修改validate_form.jsp,代码如下:

<s:form v alidate="true"action="new_v alidate"namespace="/test">

<s:textfieldname="msg"label="姓名" /><s:textfieldname="age"label="年龄"/><s:textfieldname="user.name"label="姓名1" /><s:textfieldname="user.age"label="年龄1"/>

<s:submit/></s:form>

大家可以看到,最后两个<s:textfield>的name属性是user.name和user.age,正好是加了前缀的。

现在重新访问 http://localhost:8080/validation/validate_form.jsp,验证界面如图2所示。

图2

经笔者测试, 使用visitor无法以客户端验证的方式来验证user属性, 但NewValidateAction中其他的属性可以使用客户端测试。

Struts2教程 6:在 Action类中获得 HttpServletResponse 对象- 24 -

的四种方法

在struts1.x Action类的execute方法中,有四个参数,其中两个就是response和request。而Struts2 中,并没有任何参数,因此,就不能简单地从 execute 方法获得HttpServletResponse或HttpServletRequest对象了。

但在Struts2 Action类中仍然有很多方法可以获得这些对象。下面就列出四种获得这些对象的方法。

【方法1】使用Struts2 Aware拦截器这种方法需要Action类实现相应的拦截器接口。如我们要获得HttpServletResponse对

象,需要实现org .apach e.stru ts2.inte rcepto r. ServletResp onseAw are接口,代码如下:

packageaction;

importcom.opensymphony.xwork2.ActionSupport;importjavax.servlet.http.*;importorg.apache.struts2.interceptor.*;

public class MyActionextendsActionSupportimplementsServletResponseAware{

privatejavax.servlet.http.HttpServletResponsresponse;
//
获得HttpServletResponse对象
public v oid setServ letResponse(HttpServ letResponsresponse){

this.response= response;}

public String execute()throws Exception{

response.getWriter().write实("现ServletResponseAwar接e口");}

}

在上面的代码中, MyAction实现了一个 ServletResponseAware 接口,并且实现了setServletResponse方法。如果一个动作类实现了ServletResponseAware接口,Struts2在调用execute方法之前,就会先调用setServletResponse方法,并将response参数传入这个方法。如果想获得 HttpServletRequest、HttpSession和Cookie等对象,动作类可以分别实ServletRequestAware SessionAware CookiesAware 等接口。这些接口都在org.apache.struts2.interceptor包中。

如果要获得请求参数,动作类可以实现 org.apache.struts2.interceptor. ParameterAware接口, 但如果只想判断某个参数是否存在, 也可以实co现m.opensymphony.xwork2.interceptor.

- 25 -

Param eterN am eAw are接口。这个接口有一个accep tableParam ete rN am e方法,当Struts2获得一个请求参数时,就会调用一次。读者可以在这个方法中将所有的请求参数记录下来,以便以后使用。这个方法的定义如下:

boolean acceptableParameterName(String parameterName);

【方法2】使用RequestA ware拦截器

这种方法和第 1 种方法类似。动作类需要实现一个
org .apach e.stru ts2.inte rcepto r. Reque stAw are接口。 所不同的是Requ estAw are将获得一个

com.opensymphony.xwork2.util.OgnlValueStack对象,这个对象可以获得request及其他的一些信息。代码如下所示:

packageaction;

importjava.util.Map;
importorg.apache.struts2.*;importcom.opensymphony.xwork2.ActionSupport;importjavax.servlet.http.*;importcom.opensymphony.xwork2.util.*;importorg.apache.struts2.interceptor.*;

public class FirstActionextendsActionSupportimplementsRequestAware{

privateMap request;
priv ateHttpServ letResponseresponse;

public v oid setRequest(Maprequest){

this.request= request;}

public String execute()throws Exception{

jav a.util.Set<String>keys = request.keySet();
//
枚举所有的key值。实际上只有一个key:struts.valueStackfor(Stringkey: keys)

response

System.out.println(key);
//
获得OgnlValueStack对象
OgnlValueStackstack= (OgnlValueStack)myRequest.get("struts.valueStack");
//
获得HttpServletResponse对象
response =

(HttpServletResponse)stack.getContext().get(StrutsStatics.HTTP_RESPONSE);response.getWriter().write实("现RequestAware接口");

}}

- 26 -

我们也可以使用StrutsStatics.H TTP_REQU EST、StrutsStatics. PAGE_CON TEX T来获得H ttpServletRequest和PageCon tex t对象。这种方法有些麻烦,一般很少用,读者可以作为一个参考。

【方法3】使用ActionContext类
这种方法比较简单, 我们可以通过org.apache.struts2.A
cti onContext类的get方法获得相应

的对象。代码如下:

HttpServletResponse response(HttpServletResponse) =ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);HttpServletRequest request(HttpServletRequest) =ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);

【方法4】使用Servl etA ctionContext类

Struts2为我们提供了一种最简单的方法获得H ttpServletResponse 及其他对象。 这就是org.apache.struts2.ServletActionContext类。我们可以直接使用 ServletActionContext类的getRequest、getResponse方法来获得 HttpServletRequest、HttpServletResponse对象。代码如下:

HttpServletResponse response = ServletActionContext.getResponse()

response.getWriter().write("hello world");

  从这四种方法来看,最后一种是最简单的,读者可以根据自己的需要和要求来选择使用
哪一种方法来获得这些对象。

Struts2教程7:上传任意多个文件一、上传单个文件

上传文件是很多Web程序都具有的功能。 在Struts1.x中已经提供了用于上传文件的组件。而在Struts2中提供了一个更为容易操作的上传文件组件。所不同的是, Struts1.x的上传组件需要一个ActionForm来传递文件,而Struts2的上传组件是一个拦截器(这个拦截器不用配置,是自动装载的) 。在本文中先介绍一下如何用struts2上传单个文件,最后介绍一下用stru ts2上传任意多个文件。

要用Struts2实现上传单个文件的功能非常容易实现, 只 要使用普通的Action即可。但 为了获得一些上传文件的信息,如上传文件名、上传文件类型以及上传文件的 Stream对象,就需要按着一定规则来为Action类增加一些getter和setter方法。

在Struts2中,用于获得和设置 java.io.File对象(Struts2将文件上传到临时路径,并使用java.io. File打开这个临时文件) 的 方法是getU pload和setU pload。 获 得和设置文件名的方法是 getU ploadFileN am e和 setU ploadFileN am e,获得和设置上传文件内容类型的方法是getU ploadConte ntT yp e和setU ploadConte ntT ype。下面是用于上传的动作类的完整代码:

- 27 -

packageaction;

importjava.io.*;importcom.opensymphony.xwork2.ActionSupport;

public class UploadActionextendsActionSupport{

privateFileupload;privateStringfileName;privateStringuploadContentType;

public String getUploadFileName(){

returnfileName;}

public voidsetUploadFileName(StringfileName){

this.fileName= fileName;}

public File getUpload(){

return upload;}

public v oid setUpload(Fileupload){

this.upload= upload;}

public voidsetUploadContentType(StrincgontentType){

this.uploadContentType=contentType;}

public String getUploadContentType(){

returnthis.uploadContentType;}

public String execute()throws Exception{

java.io.InputStreamis = new java.io.FileInputStream(upload);

- 28 -

java.io.OutputStreamos = new java.io.FileOutputStream("d:\\upload\\+" fileName);byte buffer[] = new byte[8192];
int count= 0;
while((count= is.read(buffer))> 0)

{
os.write(buffer,0,count);

}
os.close();is.close();returnSUCCESS;

}}

在ex ecute方法中的实现代码就很简单了,只是从临时文件复制到指定的路径(在这里是d:\u pload ) 中。 上传文件的临时目录的默认值是javax .servlet.co ntex t.tem pdir的值, 但可以通过struts.properties(和struts.xml在同一个目录下) 的struts.multipart.saveDir属性设置。Struts2上传文件的默认大小限制是2M(2097152字节) ,也可以通过struts.properties文件中的struts.multipart.maxSize修改,如struts.multipart.maxSize=2048 表示一次上传文件的总大小不能超过2K字节。

下面的代码是上传文件的JSP页面代码:

<%@
<%@ "%>

<html><head>

<title>上传 单个文 件</title></head>

<body>
<s:form action="upload"namespace="/test"

enctype="multipart/form-data"><s:filename="upload"label="输入要上传的文件名" /><s:submitvalue="上传" />

</s:form>

</body></html>

也可以在success.jsp页中通过<s:property>获得文件的属性(文件名和文件内容类型) ,代码如下:

<s:property value="uploadFileName"/>

page

language="java"import="java.util.*"pageEncoding="GBK%" >

taglib

prefix="s"

uri="/struts-tags

- 29 -

二、上传任意多个文件

在Struts 2 中, 上传任意多个文件也非常容易实现。 首先, 要想上传任意多个文件, 需要在客户端使用DOM技术生成任意多个<inputtype=”file” />标签。name属性值都相同。代码如下:

<html>

<head>
<scriptlanguage="jav ascript">

functionaddComponent(){

uploadHTML= type='file' name='upload'/>");document.getElementById("files").appendChild(uploadHTML);

"( < p / > " ) ;document.getElementById("files").appendChild(uploadHTML);

}</script>

</head><body>

<inputtype="button"onclick="addComponent();"value="添加文件" /><br/>
<form onsubmit="returntrue;" action="/struts2/test/upload.action"

method="post"enctype="multipart/form-data"><spanid="files"><inputtype='file'name='upload'/>

<p/></span>

<inputtype="submit"value="上传" /></form>

</body>

</html>

上面的javascript代码可以生成任意多个 <input type=’file’>标签,name的值都为file(要注意的是,上面的javascript代码只适合于IE浏览器,firefox等其他浏览器需要使用他的代码) 。 至 于A cti on类, 和 上传单个文件的A cti on类基本一至, 只 需要将三个属性的类型改为List即可。代码如下:

packageaction;

importjava.io.*;importcom.opensymphony.xwork2.ActionSupport;

public class UploadMoreActionextendsActionSupport{

var

document.createElement

"( < i n p u t

uploadHTML=

document.createElement

- 30 -

privatejava.util.List<File>uploads;privatejava.util.List<String>fileNames;privatejava.util.List<String>uploadContentTypes;

publicjava.util.List<String>getUploadFileName(){

returnfileNames;}

public voidsetUploadFileName(java.util.List<Stringf>ileNames){

this.fileNames= fileNames;}

publicjava.util.List<File>getUpload(){

return uploads;}

public voidsetUpload(java.util.List<File>uploads){

this.uploads= uploads;}

public voidsetUploadContentType(java.util.List<Stringc>ontentTypes){

this.uploadContentTypes= contentTypes;}

publicjava.util.List<String>getUploadContentType(){

returnthis.uploadContentTypes;}

public String execute()throws Exception{

if (uploads!= null){

int i = 0;
for (; i < uploads.siz e();i++){

java.io.InputStreamis = newjava.io.FileInputStream(uploads.get(i));java.io.OutputStreamos = new java.io.FileOutputStream(

"d:\\upload\\"+ fileNames.get(i));byte buffer[]= new byte[8192];

- 31 -

int count= 0;
while ((count = is.read(buffer))> 0){

os.write(buffer,0,count);}

os.close();

is.close();}

}

returnSUCCESS;}

}

在execute 方法中,只是对L i st对象进行枚举,在循环中的代码和上传单个文件时的代码基本相同。如果读者使用过struts1.x的上传组件,是不是感觉Struts2的上传功能更容易实现呢?在Struts1.x中上传多个文件时,可是需要建立带索引的属性的。而在Struts2中,就是这么简单就搞定了。图1是上传任意多个文件的界面。

图1

在本文中给出了用Struts2上传任意多个文件的一个方法, 如 果哪位读者有更好的方法,请跟贴!

- 32 -

Struts2教程8:拦截器概述

Struts2的拦截器和Servlet过滤器类似。 在执行Action的execute方法之前, Struts2会首先执行在 st r u t s.x ml 中引用的拦截器,在执行完所有引用的拦截器的intercept方法后,会执行Action的ex ecu te方法。

Struts2拦截器类必须从com.opensymphony.xwork2.interceptor.Intercepto接r口继承,在I ntercepter接口中有如下三个方法需要实现:

v oid destroy();

v oid init();
String intercept(ActionInv ocationinv ocation)throws Exception;
其中in tercept方法是拦截器的核心方法,所有安装的拦截器都会调用之个方法。在Struts2中已经在struts-default.xml中预定义了一些自带的拦截器,如 timer、params等。 如 果在<package> 标签中继承stru ts-defau lt, 则 当前package 就会自动拥有struts-default.xml中的所有配置。代码如下:

<package name="demo" extends="struts-default" > ... </package>

在struts-default.xml中有一个默认的引用,在默认情况下(也就是 <action>中未引用拦截器时)会自动引用一些拦截器。这个默认的拦截器引用如下:

<default-interceptor-renf ame="defaultStack"/>

<interceptor-stackname="defaultStack"><interceptor-refname="exception"/><interceptor-refname="alias"/><interceptor-refname="serv letConfig"/><interceptor-refname="prepare"/><interceptor-refname="i18n"/><interceptor-refname="chain"/><interceptor-refname="debugging"/><interceptor-refname="profiling"/><interceptor-refname="scopedModelDriv en"/><interceptor-refname="modelDriven"/><interceptor-refname="fileUpload"/><interceptor-refname="checkbox"/><interceptor-refname="staticParams"/><interceptor-refname="params">

- 33 -

<paramname="excludeParams">dojo\..*</param></interceptor-ref>

<interceptor-refname="conversionError"/><interceptor-refname="v alidation">

<paramname="excludeMethods">input,back,cancel,browse</param></interceptor-ref>

<interceptor-refname="workflow"><paramname="excludeMethods">input,back,cancel,browse</param>

</interceptor-ref></interceptor-stack>

上面在 defaultStack中引用的拦截器都可以在 <action>中不经过引用就可以使用(如果在<action>中引用了任何拦截器后, 要使用在defaultStack中定义的拦截器, 也需要在<action>中重新引用,在后面将详细讲解) 。

下面我们来看几个简单的拦截器的使用方法。一、记录拦截器和exec ut e 方法的执行时间(tim er)

timer Struts2 中最简单的拦截器,这个拦截器对应的类是com.opensymphony.xwork2.interceptor.TimerIntercepto。r 它的功能是记录 execute方法和其他拦截器(在timer后面定义的拦截器) 的intercept方法执行的时间总和。 如下面的配置代码所示:

<action name="first"class="action.FirstAction">

<interceptor-refname="logger"/>

<interceptor-refname="timer"/></action>

由于在tim er后面没有其他的拦截器定义, 因 此,tim er 只能记录ex ecute方法的执行时间,在访问first动作时,会在控制台输出类似下面的一条信息:

信息: Executed action [/test/first!execute] took 16 ms.

在使用ti mer拦截器时, 需 要commons-l oggi ng.j ar的支持。 将l ogger引用放到ti mer的后面,就可以记录logger拦截器的intercept方法和Action的execute方法的执行时间总和,代码如下:

<action name="first"class="action.FirstAction">

<interceptor-refname="timer"/>

<interceptor-refname="logger"/></action>

大家可以使用如下的Action类来测试一下timer拦截器:packageaction;

- 34 -

importcom.opensymphony.xwork2.ActionSupport;public class FirstActionextends ActionSupport
{

public String execute()throws Exception{

Thread.sleep(1000);// 延迟1秒return null;

}}

如果只记录ex ecute方法的执行时间,一般会输出如下的信息:信息: Executed action [/test/first!execute] took 1000 ms.二、通过请求调用Action的setter方法(params)

当客户端的一个form向服务端提交请求时,如有一个textfield,代码如下:

<s:form action="first"namespace="/test">

<s:textfieldname="name"/>

<s:submit/></s:form>

在提交后,Struts2将会自动调用first动作类中的setName方法,并将name文本框中的值通过setN am e方法的参数传入。 实际上,这个操作是由param s 拦截器完成的,param s 对应的类是com.opensymphony.xwork2.interceptor.ParametersInterceptor。由于 params已经在defa ultStack中定义, 因此,在未引用拦截器的<action> 中是会自动引用param s的, 如下面的配置代码,在访问first动作时,Struts2是会自动执行相应的setter方法的。

<action name="first" class="action.FirstAction">... ...

</action>但如果在<action>中引用了其他的拦截器,就必须再次引用params拦截器,Struts2才能调

用相应的sette r方法。如下面的配置代码所示:

<action name="first"class="action.FirstAction">

<interceptor-refname="timer"/>

<interceptor-refname="params"/></action>

- 35 -

三、通过配置参数调用 Action的setter方法(static-params)

static-params拦截器可以通过配置 <params>标签来调用 Action类的相应的 setter方法,是 类 的sta应tic-对para器ms 截 拦

com.opensymphony.xwork2.interceptor.StaticParametersInterceptor。下面配置代码演示了如何使用static-params拦截器:

<action name="first"class="action.FirstAction">

<interceptor-refname="timer"/><paramname="who">比尔</param><interceptor-refname="params"/><interceptor-refname="static-params"/>

</action>
如果first动作使用上面的配置, 在访问first动作时,Struts2会自动调用setWho方法将“比尔”

作为参数值传入setWho方法。四、使用拦截器栈

为了能在多个动作中方便地引用同一个或几个拦截器, 可以使用拦截器栈将这些拦截器作为一个整体来引用。拦截器栈要在 <package>标签中使用 <interceptors> 和子标签<interceptor-stack>来定义。代码如下:

<packagename="demo"extends="struts-default">

<interceptors><interceptor-stackname="mystack">

<interceptor-refname="timer"/><interceptor-refname="logger"/><interceptor-refname="params"/><interceptor-refname="static-params"/>

</interceptor-stack></interceptors>

<action name="first"class="action.FirstAction"><paramname="who">比尔</param><interceptor-refname="mystack"/>

</action></package>

       可以象使用拦截器一样使用拦截器栈,如上面代码所示。

Struts2教程9:实现自已的拦截器
在上一篇中介绍了 Struts2 拦截器的原理,在这一篇中我们将学习一下如何编写自己的拦截

- 36 -

器。一、拦截器的实现

实现一个拦截器非常简单。 实 际上, 一 个拦截器就是一个普通的类, 只 是这个类必须实现com.opens ymphony. xwo rk2.i nterceptor . Interceptor接口。Interceptor接口有如下三个方法:

public interfaceInterceptorextendsSerializable

{
v oid destroy();

v oid init();

String intercept(ActionInv ocationinv ocation)throws Exception;}

其中init和destroy 方法只在拦截器加载和释放(都由 Struts2自身处理)时执行一次。而intercept方法在每次访问动作时都会被调用。Struts2在调用拦截器时, 每个拦截器类只有一个对象实例, 而 所有引用这个拦截器的动作都共享这一个拦截器类的对象实例, 因 此, 在实现I ntercepto r接口的类中如果使用类变量,要注意同步问题。

下面我们来实现一个简单的拦截器, 这 个拦截器通过请求参数action 指定一个拦截器类中的方法,并调用这个方法(我们可以使用这个拦截器对某一特定的动作进行预处理) 。如果方法不存在,或是action参数不存在,则继续执行下面的代码。如下面的U RL:

http://localhost:8080/struts2/test/interceptor.action?action=test

访问上面的url后,拦截器会就会调用拦截器中的 test方法,如果这个方法不存在,则调用invocation.invoke方法,invoke方法和Servlet过滤器中调用 FilterChain.doFilter方法类似,如果在当前拦截器后面还有其他的拦截器,则 invoke方法就是调用后面拦截器的 intercept方法,否则,invoke会调用Action类的execute方法(或其他的执行方法) 。

下面我们先来实现一个拦截器的父类 ActionInterceptor。这个类主要实现了根据 action参数值来调用方法的功能,代码如下:

packageinterceptor;

importcom.opensymphony.xwork2.ActionInvocation;importcom.opensymphony.xwork2.interceptor.Interceptor;importjavax.servlet.http.*;
importorg.apache.struts2.*;

public classActionInterceptorimplementsInterceptor{

protectedfinalStringINVOKE= "##invoke";

- 37 -

public v oid destroy(){

System.out.println("destroy");}

public v oid init(){

System.out.println("init");}

public String intercept(ActionInvocationinvocation)throwsException{

HttpServletRequesrtequest= ServletActionContext.getRequest();Stringaction = request.getParameter("action");System.out.println(this.hashCode());
if (action!= null)

{
try

{
java.lang.reflect.Methodmethod= this.getClass().getMethod(action);String result = (String)method.invoke(this);
if(result != null)
{

if(!result.equals(INVOKE))return result;

}else

return null;

}
catch (Exceptione){
}

}

returninvocation.invoke();}

}

从上面代码中的interc ept方法可以看出,在调用action所指定的方法后,来判断返回值。可能发生的情况有三种:

1.2.3.

返回值为null,执行return null。
返回值为INVOKE,执行return invockation.invoke()。
其他情况,执行return result。 result表示指定方法的返回值,如上面代码所示。

在实现完上面的拦截器父类后,任何继承于ActionInterceptor类的拦截器都可以自动根- 38 -

据acti on的参数值调用自身的相应方法。下面我们来实现一个拥有两个动作方法的拦截器类。代码如下:

packageinterceptor;

importjavax.servlet.http.HttpServletResponse;importorg.apache.struts2.ServletActionContext;

public class MultiMethodInterceptoer xtendsActionInterceptor{

public String test() throws Exception{

HttpServletResponseresponse= ServletActionContext.getResponse();response.getWriter().println("invoktest");
returnthis.INVOKE;

}

public String print() throws Exception{

HttpServletResponseresponse= ServletActionContext.getResponse();r e s p o n s e . g e t W r i t e r ( ) . p r i n t l n ( " i n v o k pe r i n t " ) ;

return null;}

}

test 和pri nt

test方法返回了 INVOKE,因此,在执行完这个方法后, Struts2会接着调用其他拦截器的interc ept方法或Action类的ex ecute方法。而prin t方法在执行完后,只是返回了null,而不再调用其他的方法了,也就是访问如下的url 时,动作的ex ecute方法将不会执行:

http ://localho st:8080 /struts 2/test/d dd.actio n?action=p rint

下面我们来实现一个A cti on类,代码如下:packageaction;

importorg.apache.struts2.*;importcom.opensymphony.xwork2.ActionSupport;

publicclassInterceptorActionextendsActionSupport{

public String abcd()throws Exception{

- 39 -

ServletActionContext.getResponse().getWriter().println("inv okeabcd");

return null;}

}
在这个Action类中,只有一个abcd方法,实际上,这个方法相当于ex ecute方法,在下面会

设置动作的method属性为abcd。 下面我们来在struts.xml中定义拦截器类和动作, 代码如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><packagename="demo"extends="struts-default"namespace="/test">

<interceptors>
< i n t e r c e p t o r n a m e = " m e t h o d " c l a s s = " i n t e r c e p t o r . M u l t i M e t h o d I n t e r c e p t o / r >"

<interceptor-stackname="methodStack"><interceptor-refname="method"/><interceptor-refname="defaultStack"/>

</interceptor-stack></interceptors>

<actionname="interceptor"class="action.InterceptorActionm" ethod="abcd"><interceptor-refname="methodStack"/>

</action></package>

</struts>在配置上面的methodStack拦截器时要注意,最好在后面引用defaultStack,否则很多通过

拦截器提供的功能将失去。OK,现在访问如下的U RL:

http://localhost:8080/struts2/test/ddd.action?action=test

在浏览器中将会出现如下的字符串:

invoke testinvoke abcd

而如果访问http://localhost:8080/struts2/test/ddd.action?action=print, 将会只出现如下的字符串:

- 40 -

invoke print

大家可以看出, 访 问这个url 时并没有调用abcd方法。 如 果随便指定的action 值的话, 则 只调用abcd方法, 如访问http ://localho st:8080 /struts 2/test/d dd.actio n?action =aaa, 就只会输出invoke abcd。

二、拦截器的参数

我们在使用很多Struts2内置的拦截器时会发现有很多拦截器都带参数,当然。我们自己做的拦截器也可以加上同样的参数。 有两个参数比较常用, 这两个参数是includeMethods和excludeMethods,其中includeMethods指定了拦截器要调用的 Action类的执行方法(默认是execute) ,也就是说,只有在includeMethods中指定的方法才会被 Struts2调用,而ex cludeMethods恰恰相反,在这个参数中指定的执行方法不会被Struts2调用。如果有多个方法,中间用逗号(,)分隔。在Struts2中提供了一个抽象类来处理这两个参数。这个类如下:

com.opensymphony.xwork2.interceptor.MethodFilterInterceptor如有继承于这个类的拦截器类都会自动处理includeMethods和excludeMethods参数,如下

面的拦截器类所示:

packageinterceptor;

importcom.opensymphony.xwork2.ActionInvocation;importcom.opensymphony.xwork2.interceptor.*;

public class MyFilterInterceptorextendsMethodFilterInterceptor{

privateStringname;public String getName(){

return name;}

public v oid setName(Stringname){

this.name= name;}

@Ov errideprotectedStringdoIntercept(ActionInvocationinvocation)throwsException{

System.out.println("doIntercept");System.out.println(name);returninvocation.invoke();

}

- 41 -

}

MethodFilterInterceptor的子类需要实现 doIntercept方法(相当于 Interceptor的intercept方法) ,如上面代码所示。在上面的代码中还有一n个ame属性,是为了读取拦截器的name属性而设置的,如下面的配置代码所示:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstruts PUBLIC"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">

<struts><packagename="demo"extends="struts-default"namespace="/test">

<interceptors>
< i n t e r c e p t o r n a m e = " m e t h o d " c l a s s = " i n t e r c e p t o r . M u l t i M e t h o d I n t e r c e p t o / r >"

<interceptorname="filter"class="interceptor.MyFilterInterceptor"><paramname="includeMethods">abcd</param><paramname="name">中国</param>

</interceptor><interceptor-stackname="methodStack">

<interceptor-refname="method"/><interceptor-refname="filter"/><interceptor-refname="defaultStack"/>

</interceptor-stack></interceptors>

<actionname="interceptor"class="action.InterceptorActionm" ethod="abcd"><interceptor-refname="methodStack"/>

</action></package>

</struts>

再次访问 http://localhost:8080/struts2/test/ddd.action?action=test, Struts2 就会调用MyFilterInterceptor的doIntercept方法来输出name属性值。如果将上面的 includeMethods参数值中的abcd去掉,则Action类的abcd方法不会被执行。

Struts2教程10:国际化

国际化的作用就是根据不同国家的用户在访问Web或其他类型的程序时,将各种信息以本地的常用形式显示出来,如界面信息在中国,就会显示中文信息,在以英文为主的国家里,就会显示英文信息。还有就是一些信息的格式,如日期格式等。

从属性文件中获得字符串信息是国际化的基本应用。在 Struts2中使用的属性文件就是Java- 42 -

属性文件, 扩展名为pro pertie s。 在Struts2中的属性文件可以有很多默认的位置,Struts2 可按如下的顺序(或步骤)来定位属性文件:

1. ActionClass.properties:属性文件名和动作类同名。 Struts2会首先查询与当前访问的动作类同名,并且和ActionClass.class在同一个目录下的属性文件。

2. BaseClass.properties:BaseClass表示动作类的基类。所有动作类都会查找Object.properties文件(因为Object是所有Java类的基类) ,但要注意的O是bject.properties文件可不能放到当前动作类的目录中, 由于Object在java.lang包中,因此, Object.properties要放到jdk包的java"lang目录中。而对于ActionSupport.properties文件,当然也不能放到动作类的当前目录中,由于ActionSuppo rt类中com .opensym phon y.x w ork2名中,因此,需要将ActionSuppo rt.pro perti es文件放到 xw ork2.jar包中的 com \open sym phony\xw ork2目录中,由于放到 jar文件中不太方便,因此,可以使一个和当前动作类在一个目录的类先继承ActionSupport,然后所有的动作类都继承于这个类。代码如下:

public class MyActionSupportextends ActionSupport

{
......

}
public classActionClassextendsMyActionSupport{

......}

这样的话,只要存在一个MyActionSupport.properties,在当前目录下的所有动作类都会读取这个文件。

3. Interface.properties:这类文件和BaseClass.properties类似,Interface表示动作类实现的接口。

4. 如果动作类实现了ModelDriven, 那么重复第1步。

5. package.properties:大家要注意。这个文件就叫 package.properties。不象Interface和BaseClass都是泛指。 这 个文件可以放到当前动作类的包的任何一层目录下。 如当前动作类在action.test包中。那么package.properties可以放到action目录中,也可以放到action"test目录中。Struts2会从离动作类最近的位置开始查找packa ge.pro perti es文件。

6. 搜索il8n资源信息
7. 查找全局资源属性文件

如下面是一个动作类

packageaction.test;

importorg.apache.struts2.*;importcom.opensymphony.xwork2.ActionSupport;

- 43 -

public classInternationaliz ingextendsActionSupport{

public String execute()throwsException{

return"forward";}

}

在action\test目录下有一个Internationaliz ing.properties文件,内容如下:delete = 删除
save = 保存
我们可以在jsp文件中使用如下几种方法取出资源信息:

<s:property value="getText('delete')"/><s:text name="save" />

3. 使用<s:il8n>标签。这个标签可以直接定位属性文件,如INF\classes\test目录下,内容和Internationaliz ing.properties一样,则可以使用如下的代码读取abc.properties的内容:

abc.properties在WEB-

<%@

taglib

prefix="s"

uri="/struts-tags"

%>

<s:i18n name="test.abc"><s:textname="save"/><s:textname="delete"/>

</s:i18n>

当然,我们也可以使用全局的属性文件,在stru ts.pro pertie s文件,内容如下:

WEB-INF"classes 目录下建立一个

struts.custom.i18n.resources=my
在WEB-INF\classes目录下建立一个my.properties文件, 当Struts2按着上述的顺序没有找到

相应的属性文件时,最后就会考虑寻找全局的属性文件,因此,就会找到m y. prope rties。

还可以通过属性文件名来让Struts2按着客户端浏览器的语言环境来找符合某种语言的属性文件。 如有三个属性文件language.properties、language_en.properties、language_zh.properties。如果客户端的语言是中文, Struts2 就会读 language_zh.properties,如果是英文,就会读language_en.properties。如果这两个文件的某个不存在,就会读 language.properties。读者可通过IE的[工具]->[Internet]->[语言]来测试客户端浏览器的语言,如图1所示:

- 44 -

图1

2008年7月5日

- 45 -

一、准备工作及实例 3 1.解压struts-2.1.6-all.zip 3 2.六个基本包 3 3.初识struts2配置文件 4 (1).web.xml文件 4 (2).struts.xml文件 4 (3).struts.properties(参default.properties) 4 (4)struts-default.xml 4 (5)其它配置文件 4 4.让MyEclipse提示xml信息 4 5.如何使用alt+/提示 4 6.实例 4 7.开启struts2自带的开发模式常量 6 8.vo传参模式 7 9.ModerDriven传参模式(不建议采用) 7 10.为什么要使用struts2代替struts1.x 7 二、struts.xml配置及例程 7 1.配置文件的优先级 7 2.配置形式 8 3.package配置相关 8 4.分工合作include:指定多个配置文件 10 5.tomcat认证访问 10 6.初识拦截器 11 7.Action中的method属性 12 8.使用ForwardAction实现页面屏蔽。 13 8.使用default-Action配置统一访问 14 小结Action 14 9.使用通配符 14 10.使用0配置:ZERO Annotation 15 11.Result配置详解 15 探讨type类型: 16 Type类型值 16 作用说明 16 对应类 16 chain 16 用来处理Action链 16 com.opensymphony.xwork2.ActionChainResult 16 dispatcher 16 用来转向页面,通常处理JSP 16 org.apache.struts2.dispatcher.ServletDispatcherResult 16 redirect 16 重定向到一个URL 16 org.apache.struts2.dispatcher.ServletRedirectResult 16 redirectAction 16 重定向到一个Action 16 org.apache.struts2.dispatcher.ServletActionRedirectResult 16 plainText 16 显示源文件内容,如文件源码 16 org.apache.struts2.dispatcher.PlainTextResult 16 freemarker 16 处理FreeMarker模板 16 org.apache.struts2.views.freemarker.FreemarkerResult 16 httpheader 16 控制特殊http行为的结果类型 16 org.apache.struts2.dispatcher.HttpHeaderResult 16 stream 16 向浏览器发送InputSream对象,通常用来处理文件下载,还可用于返回AJAX数据。 16 org.apache.struts2.dispatcher.StreamResult 16 velocity 16 处理Velocity模板 16 org.apache.struts2.dispatcher.VelocityResult 16 xslt 16 处理XML/XLST模板 16 org.apache.struts2.views.xslt.XSLTResult 16 全局result: 17 动态Result:了解 18 12.异常处理 18 三、在Action获取Scope对象 19 方式一、与Servlet解耦合的非IOC方式 20 方式二、与Servlet解耦合的IOC方式 21 方式三、与Servlet耦合的非IOC方式 21 方式四、与Servlet耦合的IOC方式 22 四、OGNL与ValueStack(VS) 22 1.值栈入门 22 2.OGNL入门 24 3.普通方法访问 24 4.静态方法访问 24 5.默认类Math的访问 24 6.调用普通类的构造方法 25 7.集合对象初步 25 8.集合对象进阶 25 9.N语法top语法 26 10.获取Stack Context中的信息 26 11.总结$ # %的区别 27 12.总结OGNL[重点] 27 五、拦截器 28 1.概述strust2中的拦截器 28 2.自定义拦截器 28 方式一,实现Interceptor接口。 28 方式二、继承AbstractInterceptor抽象类 29 方式三、继承MethodFilterInteceptor类 30 3.使用来MethodFilterInterceptor灵活拦截 32 4.使用默认的execAndWait拦截器 33 5.
概述 4 Framework概念 4 Struts的概念和体系结构 5 Struts的与Web App的关系 5 Struts的体系结构 5 从视图角度(View) 6 从模型角度(Model) 6 从控制器角度(Controller) 6 Struts的基本组件包 6 Struts framework的工作原理和组件 7 Struts ActionServlet控制器对象 8 Struts Action Classes 8 Struts Action Mapping 9 使用ActionForward导航 10 Struts ActionForm Bean捕获表单数据 11 Struts的其他组件 12 Validation Framework for Struts 12 Struts TagLib 12 BeanUtils 12 Collections 13 Digester 13 Struts配置文件简介 13 有关Struts Controller及其相关的的配置描述 13 有关struts tag lib的配置描述 14 有关Struts Action Mapping的配置描述 14 Form-bean元素 15 Action元素 15 Struts高级特性(Struts Advanced Feature) 17 验证 17 使用异常处理声明 19 使用应用模块(Application Modules) 21 把JSP放到WEB-INF后以保护JSP源代码 22 使用 Prebuilt Action类提升开发效率 23 Struts标记库 25 定制JSP标记 25 资源束 26 Bean标记 27 Bean复制标记 27 定义脚本变量的标记 28 显示Bean属性 29 消息标记和国际化 29 逻辑标记 30 条件逻辑 30 重复标记 32 转发和重定向标记 33 HTML标记 34 显示表单元素和输入控件 34 显示错误信息的标记 38 其他HTML标记 39 模板标记 39 一个简单的示例 41 Struts的安装 41 第一个实验:简单的JSP页 41 第二个实验:struts的国际化 42 struts中的Forms 45 struts:介绍ActionForm 49 分离Book和BookForm的一个好方法 51
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值