光辉飞翔

导航

 

Action 跟 Actionsupport 的区别

 

 

Action 跟 Actionsupport 的区别 当我们在写action的时候,可以实现Action接口,也可以继承Actionsupport这个类.到底这两个有什么区别呢?
Action接口有: public static final java.lang.String SUCCESS = "success"; public static final java.lang.String NONE = "none"; public static final java.lang.String ERROR = "error"; public static final java.lang.String INPUT = "input"; public static final java.lang.String LOGIN = "login";
public abstract java.lang.String execute() throws java.lang.Exception;
而Actionsupport这个工具类在实现了Action接口的基础上还定义了一个validate()方法,重写该方法,它会在execute()方法之前执行,如校验失败,会转入input处,必须在配置该Action时配置input属性。
另外,Actionsupport还提供了一个getText(String key)方法还实现国际化,该方法从资源文件上获取国际化信息.
这样在自定义标签时可以定义一个变量为new actionsupport对象实现国际化。
ActionSupport类的作用 struts2不要求我们自己设计的action类继承任何的struts基类或struts接口,但是我们为了方便实现我们自己的action,大多数情况下都会继承com.opensymphony.xwork2.ActionSupport类,并重写此类里的public String execute() throws Exception方法。因为此类中实现了很多的实用借口,提供了很多默认方法,这些默认方法包括国际化信息的方法、默认的处理用户请求的方法等,这样可以大大的简化Acion的开发。 Struts2中通常直接使用Action来封装HTTP请求参数,因此,Action类里还应该包含与请求参数对应的属性,并且为属性提供对应的getter和setter方法。
========================== <s:property> 关于struts2标签取值的备忘录
取值要通过<s:property value="" />或在任意的<s:/>标签内使用%{};
当Action的valueStack中有该属性的值时,只需直接使用该属性的名字即可;
当Action的valueStack中没有该属性的值时,比如在session,application范围中的属性值时,需要加#或者#attr.;
例子: 假设某Action中有person成员变量,在application中存在company属性 那么我们可以通过以下方法取值: <s:property value="person.name" /> <s:property value="#person.name" /> <s:property value="company.name" /> //无法取到,因为company不在action的valueStack中 <s:property value="#company.name" />
<s:textfield name="person.name" value="person.name" /> //错误,value会直接显示person.name字样 <s:textfield name="person.name" value="%{person.name}" /> <s:textfield name="person.company.name" value="%{#company.name}" /> <s:textfield name="person.company.name" value="%{#attr.company.name}" />
===================== 一次Action调用都会创建一个ActionContext 调用:ActionContext context = ActionContext.getContext() ValueStack由OGNL框架实现 可以把它简单的看作一个集合
Stack Object:放入stack中的对象,一般是action Stack Context(map):stack上下文,它包含一些列对象,包括request/session/attr/application map等。
EL:存取对象的任意属性,调用对象的方法,遍历整个对象结…
=========== WebWork2.2笔记(二)ActionSupport及其他基础知识 绝大多数情况下,WebWork不是直接实现com.opensymphony.xwork.Action接口,而是扩展 com.opensymphony.xwork.ActionSupport类。ActionSupport实现了除Action以外的其他几个接口,主要的几个接口是: com.opensymphony.xwork.Validateable com.opensymphony.xwork.Validateaware com.opensymphony.xwork.TextProvider com.opensymphony.xwork.LocaleProvider 这些接口主要是提供给WebWork的拦截器使用的,接口配合拦截器可以实现AOP功能。比如Validateable接口和Validateaware 接口配合DefailtWorkflowInterceptor就可以实现对用户输入进行检验的功能,当用户调用Action时,首先执行 Validateable接口定义的validate()方法,如果在这个方法中用户使用接口Validateaware中的方法设置了错误信息,则 DefaultWorkflowInterceptor会自动终止Action的执行,并产生一个INPUT的result,只有没有任何错误信息才会执行Action的剩余部分。
前面我们定义的xwork.xml配置文件中包含了一句:<include file="webwork-default.xml">,这个webwork-default.xml包含在webwork的jar包中。 webwork-default.xml预建了很多常用的result-type、interceptor和interceptor-stack。其中 interceptor和interceptor-stack用于定义webwork将要使用那些拦截器。interceptor定义一个拦截器,而 interceptor定义一组拦截器。如果一个Action使用一组拦截器,则这些拦截器将以其定义的顺序执行,可见,interceptor- stack中拦截器的顺序时很重要的。
在webwork-default.xml中有如下定义: <interceptor class="com.opensymphony.xwork.interceptor.DefaultWorkflowInterceptor" name="workflow"> ...... 而名为validationWorkflowStack和completeStack这两个预建的interceptor-stack则包含了workflow这个interceptor。
下面做一个检查用户输入的练习,如果用户没有输入内容,则让用户重新输入,并且提示错误信息。
web.xml 与上次的相同。
xwork.xml </interceptor></include> xml 代码
1. 2. "http://www.opensymphony.com/xwork/xwork-1.1.1.dtd">
3. 4. <xwork> 5. <include file="webwork-default.xml" />
6. 7. <package name="default" extends="webwork-default"> 8.
9. <default-interceptor-ref name="validationWorkflowStack"/> 10. <action name="alidateInput" class="example.ValidateInputAction"> 11. <result name="success">success.jspresult> 12. <result name="input">input.jspresult> 13. action> 14. 15. package> 16. xwork>
input.jsp xml 代码
1. <%@taglib prefix="ww" uri="/webwork"%> 2. <html> 3. <head><title>Input Somethingtitle>head> 4. <body>
5. 6. <ww:form action="alidateInput.action"> 7. <ww:textfield label="Input something:" name="something">ww:textfield>
8. <ww:submit>ww:submit> 9. ww:form> 10. 11. body> 12. html>
success.jsp xml 代码
1. <%@taglib prefix="ww" uri="/webwork"%> 2. <html> 3. <head><title>Success!title>head> 4. <body> 5. <ww:property value="something"/> 6. body> 7. html>
============================== Action的实例,总是放到value stack中。因为Action放在stack中,而stack是root(根对象),所以对Action中的属性的访问就可以省略#标记。但是,要访问 ActionContext中其它对象的属性,就必须要带上#标记,以便让OGNL知道,不是从根对象,而是从其它对象中去寻找。
Struts2中OGNL,valueStack,stackContext的学习 学习Struts2,一直不明白表单中的值是怎么传给Action的,上网查了些资料,基本了解了!下面基本是从几个人的BOLG转载过来,以后记不清了再来看~
[color=red]先看看我做的实验jsp页面 Java代码
<s:form action="hello/converter.action" method="post"> <s:textfield name="point" label="点"></s:textfield> <s:textfield name="point2" label="2"></s:textfield> <s:textfield name="point3" label="3"></s:textfield> <s:textfield name="age" label="年龄"></s:textfield> <s:textfield name="date" label="日期"></s:textfield> <s:submit name="提交"></s:submit> </s:form>
结果图(是通过<s:debug></s:debug>得到的) value stack:
[点击查看原始大小图片]
Stack context:
通过图中我们可以看到 valuestack中包括我传递的值(point,point2,point3,age,date) stack context中包括了 request application OgnlValueStack(root) session parameters 等属性
值栈(ValueStack)     Struts2将OGNL上下文设置为Struts2中的ActionContext(内部使用的仍然是OgnlContext),并将值栈设为OGNL的根对象。     我们知道,OGNL上下文中的根对象可以直接访问,不需要使用任何特殊的“标记”,而引用上下文中的其他对象则需要使用“#”来标记。由于值栈是上下文中的根对象,因此可以直接访问。那么对于值栈中的对象该如何访问呢?Struts2提供了一个特殊的OGNLPropertyAccessor,它可以自动查找栈内的所有对象(从栈顶到栈底),直接找到一个具有你所查找的属性的对象。也就是说,对于值栈中的任何对象都可以直接访问,而不需要使用 “#”。     假设值栈中有两个对象:student和employee,两个对象都有name属性,student有学号属性number,而 employee有薪水属性salary。employee先入栈,student后入栈,位于栈顶,那么对于表达式name,访问的就是student 的name属性,因为student对象位于栈顶;表达式salary,访问的就是employee的salary属性。正如你所见,访问值栈中的对象属性或方法,无须指明对象,也不用“#”,就好像值栈中的对象都是OGNL上下文中的根对象一样。这就是Struts2在OGNL基础上做出的改进。
值栈中的Action实例   Struts2框架总是把Action实例放在栈顶。因为Action在值栈中,而值栈又是OGNL中的根,所以引用Action的属性可以省略“#”标记,这也是为什么我们在结果页面中可以直接访问Action的属性的原因。
Struts2中的命名对象     Struts2还提供了一些命名对象,这些对象没有保存在值栈中,而是保存在ActionContext中,因此访问这些对象需要使用“#”标记。这些命名对象都是Map类型。   
parameters     用于访问请求参数。如:#parameters['id']或#parameters.id,相当于调用了HttpServletRequest对象的getParameter()方法。     注意,parameters本质上是一个使用HttpServletRequest对象中的请求参数构造的Map对象,一量对象被创建(在调用Action实例之前就已经创建好了),它和HttpServletRequest对象就没有了任何关系。 
request     用于访问请求属性。如:#request['user']或#request.user,相当于调用了HttpServletRequest对象的getAttribute()方法。   
session     用于访问session属性。如:#session['user']或#session.user,相当于调用了HttpSession对象的getAttribute()方法。   
application     用于访问application属性。如:#application['user']或#application.user,相当于调用了ServletContext的getAttribute()方法。   
attr     如果PageContext可用,则访问PageContext,否则依次搜索request、session和application对象。
以下是转过来的:先分清楚下ActionContext 、ValueStack 、Stack Context三者
ActionContext 一次Action调用都会创建一个ActionContext 调用:ActionContext context = ActionContext.getContext()
ValueStack 由OGNL框架实现 可以把它简单的看作一个栈(List) 。
Stack Object:放入stack中的对象,一般是action。 Stack Context(map):stack上下文,它包含一系列对象,包括request/session/attr/application map等。 EL:存取对象的任意属性,调用对象的方法,遍历整个对象结…
ActionContext是Action上下文,可以得到request session application ValueStack是值栈 存放表单中的值 Stack Context 栈上下文 也是用来存值的

struts2对OGNL上下文的概念又做了进一步扩充,在struts2中,OGNL上下文通常如下所示:
|--request
|
|--application
|
context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]
|
|--session
|
|--attr
|
|--parameters


在Struts2中,采用标准命名的上下文(Context)来处理OGNL表达式。处理OGNL的顶级对象是一个Map(也叫context map),而OGNL在这个context中就是一个顶级对象(root)。在用法上,顶级对象的属性访问,是不需要任何标记前缀的。而其它非顶级的对象访问,需要使用#标记。 Struts2框架把OGNL Context设置为我们的ActionContext。并且ValueStack作为OGNL的根对象。除value stack之外,Struts2框架还把代表application、session、request这些对象的Map对象也放到 ActionContext中去。(这也就是Struts2建议在Action类中不要直接访问Servlet API的原因,他可以通过ActionContext对象来部分代替这些(Servlet API)功能,以方便对Action类进行测试!) Action的实例,总是放到value stack中。因为Action放在stack中,而stack是root(根对象),所以对Action中的属性的访问就可以省略#标记。但是,要访问 ActionContext中其它对象的属性,就必须要带上#标记,以便让OGNL知道,不是从根对象,而是从其它对象中去寻找。 那么访问Action中的属性的代码就可以这样写
<s:property value="postalCode"/> 其它ActionContext中的非根对象属性的访问要像下面这样写: <s:property value="#session.mySessionPropKey"/> or <s:property value="#session['mySessionPropKey']"/> or <s:property value="#request['myRequestPropKey']"/> 对Collection的处理,内容就很简单。 <s:select label="label" name="name" list="{'name1','name2','name3'}" value="%{'name2'}" /> 这是处理List。这个代码在页面上建立一个下拉选项,内容是list中的内容,默认值是name2. 处理map
<s:select label="label" name="name" list="#{'foo':'foovalue', 'bar':'barvalue'}" />
需要注意的是,判断一个值是否在collection中。我们要使用in或者not in来处理。 <s:if test="'foo' in {'foo','bar'}"> muhahaha </s:if> <s:else> boo </s:else> 另外,可以使用通配符来选择collection对象的子集。 ?——所有匹配选择逻辑的元素 ^——只提取符合选择逻辑的第一个元素 $——只提取符合选择逻辑的最后一个元素 person.relatives.{? #this.gender == 'male'}
`````````````````````````````````````````````````````````````````````````````````
以下为补充摘录的一些问题:
提问:在Struts2中,如何使用自身的Tag读取Action中的变量?
Struts2自身的Tag会根据value中的OGNL表达式,在ValueStack中寻找相应的对象。因为action在 ValueStack的顶部,所以默认情况下,Struts2的Tag中的OGNL表达式将查找action中的变量。请注意,value中的内容直接是 OGNL表达式,无需任何el的标签包装。
例如:<s:property value="user.name" />
提问:在Struts2中,如何使用自身的Tag读取HttpServletRequest,HttpSession中的变量?
在上面的知识中,我们知道,Struts2中OGNL的上下文环境中,包含request,session,application等 servlet对象的Map封装。既然这些对象都在OGNL的上下文中,那么根据OGNL的基本知识,我们可以通过在表达式前面加上#符号来对这些变量的值进行访问。
例如:<s:property value="%{#application.myApplicationAttribute}" /> <s:property value="%{#session.mySessionAttribute}" /> <s:property value="%{#request.myRequestAttribute}" /> <s:property value="%{#parameters.myParameter}" />

提问:在Struts2中,如何使用JSTL来读取Action中的变量?
这是一个历史悠久的问题。因为事实上,很多朋友(包括我在内)是不使用Struts2自身的标签库,而是使用JSTL的,可能因为JSTL标签库比较少,简单易用的原因吧。
我们知道,JSTL默认是从page,request,session,application这四个Scope逐次查找相应的EL表达式所对应的对象的值。那么如果要使用JSTL来读取Action中的变量,就需要把Action中的变量,放到request域中才行。所以,早在 Webwork2.1.X的年代,我们会编写一个拦截器来做这个事情的。大致的原理是:在Action执行完返回之前,依次读取Action中的所有的变量,并依次调用request.setAttribute()来进行设置。具体的整合方式,请参考以下这篇文档:http://wiki.opensymphony.com/display/WW /Using+WebWork+and+XWork+with+JSP+2.0+and+JSTL+1.1
不过随着时代的发展,上面的这种方式,已经不再被推荐使用了。(虽然如此,我们依然可以学习它的一个解决问题的思路)目前来说,自从 Webwork2.2以后,包括Struts2,都使用另外一种整合方式:对HttpServletRequest进行装饰。让我们来看一下源码: Java代码
public class StrutsRequestWrapper extends HttpServletRequestWrapper {
/** * The constructor * @param req The request */ public StrutsRequestWrapper(HttpServletRequest req) { super(req); }
/**
* Gets the object, looking in the value stack if not found * * @param s The attribute key */ public Object getAttribute(String s) {
if (s != null && s.startsWith("javax.servlet")) { // don't bother with the standard javax.servlet attributes, we can short-circuit this
// see WW-953 and the forums post linked in that issue for more info
return super.getAttribute(s); }
ActionContext ctx = ActionContext.getContext(); Object attribute = super.getAttribute(s);
boolean alreadyIn = false; Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute"); if (b != null) { alreadyIn = b.booleanValue(); }
// note: we don't let # come through or else a request for // #attr.foo or #request.foo could cause an endless loop if (!alreadyIn && attribute == null && s.indexOf("#") == -1) {
try { // If not found, then try the ValueStack
ctx.put("__requestWrapper.getAttribute", Boolean.TRUE); ValueStack stack = ctx.getValueStack(); if (stack != null) { attribute = stack.findValue(s); } } finally {
ctx.put("__requestWrapper.getAttribute", Boolean.FALSE); } }
return attribute; } }

看到了嘛?这个类会在Struts2初始化的时候,替换HttpServletRequest,运行于整个Struts2的运行过程中,当我们试图调用 request.getAttribute()的时候,就会执行上面的这个方法。(这是一个典型的装饰器模式)在执行上面的方法时,会首先调用 HttpServletRequest中原本的request.getAttribute(),如果没有找到,它会继续到ValueStack中去查找,而action在ValueStack中,所以action中的变量通过OGNL表达式,就能找到对应的值了。
在这里,在el表达式广泛使用的今天,JSTL1.1以后,也支持直接使用el表达式。注意与直接使用struts2的tag的区别,这里需要使用el的表示符号:${}
例如:${user.name}, <c:out value="${department.name}" />
提问:在Struts2中,如何使用Freemarker等模板来读取Action中的变量以及HttpServletRequest和HttpSession中的变量?
Freemarker等模板在Struts2中有对应的Result,而在这些Result中,Freemarker等模板会根据 ValueStack和ActionContext中的内容,构造这些模板可识别的Model,从而使得模板可以以他们各自的语法对ValueStack 和ActionContext中的内容进行读取。
有关Freemarker对于变量的读取,可以参考Struts2的官方文档,非常详细:http://struts.apache.org/2.0.14/docs/freemarker.html
设值计算
Struts2中使用OGNL进行设值计算,就是指View层传递数据到Control层,并且能够设置到相应的Java对象中。这个过程从逻辑上说需要分成两步来完成:
1. 对于每个请求,都建立一个与相应Action对应的ActionContext作为OGNL的上下文环境和ValueStack,并且把Action压入ValueStack
2. 在请求进入Action代码前,通过某种通用的机制,搜集页面上传递过来的参数,并调用OGNL相关的代码,对Action进行设值。 上面的第一个步骤,在处理URL请求时完成,而第二个步骤由struts2内置的拦截器完成。
================================ Struts2教程:使用validate方法验证数据 在Struts2中最简单的验证数据的方法是使用validate。我们从ActionSupport类的源代码中可以看到,ActionSupport类实现了一个Validateable接口。这个接口只有一个validate方法。如果Action类实现了这个接口,Struts2在调用execute方法之前首先会调用这个方法,我们可以在validate方法中验证,如果发生错误,可以根据错误的level选择字段级错误,还是动作级错误。并且可使用addFieldError或addActionError加入相应的错误信息,如果存在Action或 Field错误,Struts2会返回“input”(这个并不用开发人员写,由Struts2自动返回),如果返回了“input”,Struts2就不会再调用execute方法了。如果不存在错误信息,Struts2在最后会调用execute方法。
这两个add方法和ActionErrors类中的add方法类似,只是add方法的错误信息需要一个ActionMessage对象,比较麻烦。除了加入错误信息外,还可以使用addActionMessage方法加入成功提交后的信息。当提交成功后,可以显示这些信息。
以上三个add方法都在ValidationAware接口中定义,并且在ActionSupport类中有一个默认的实现。其实,在 ActionSupport类中的实现实际上是调用了ValidationAwareSupport中的相应的方法,也就是这三个add方法是在 ValidationAwareSupport类中实现的,代码如下:
1. private final ValidationAwareSupport validationAware = new ValidationAwareSupport(); 2. 3. public void addActionError(String anErrorMessage) 4. { validationAware.addActionError(anErrorMessage); 5. } 6. public void addActionMessage(String aMessage) 7. { 8. validationAware.addActionMessage(aMessage); 9. } 10. public void addFieldError(String fieldName, String errorMessage) 11. { 12. validationAware.addFieldError(fieldName, errorMessage); 13. }
下面我们来实现一个简单的验证程序,来体验一个validate方法的使用。
先来在Web根目录建立一个主页面(validate.jsp),代码如下:
1. < %@ page language="java" import="java.util.*" pageEncoding="GBK"%> 2. < %@ taglib prefix="s" uri="/struts-tags" %> 3. < html> 4. < head> 5. < title>验证数据< /title> 6. < /head>
7. 8. < body> 9. < s:actionerror/> 10. < s:actionmessage/> 11. < s:form action="validate.action" theme="simple"> 12. 输入内容:< s:textfield name="msg"/> 13. < s:fielderror key="msg.hello" /> 14. < br/> 15. < s:submit/> 16. < /s:form> 17. < /body> 18. < /html>
在上面的代码中,使用了Struts2的tag:< s:actionerror>、< s:fielderror>和< s:actionmessage>,分别用来显示动作错误信息,字段错误信息,和动作信息。如果信息为空,则不显示。
现在我们来实现一个动作类,代码如下:
1. package action; 2. 3. import javax.servlet.http.*; 4. 5. import com.opensymphony.xwork2.ActionSupport; 6. import org.apache.struts2.interceptor.*; 7. 8. public class ValidateAction extends ActionSupport 9. { 10. private String msg; 11. public String execute() 12. { 13. System.out.println(SUCCESS); 14. return SUCCESS;
15. } 16. public void validate() 17. { 18. if(!msg.equalsIgnoreCase("hello")) 19. { 20. System.out.println(INPUT);
21. this.addFieldError("msg.hello", "必须输入hello!"); 22. this.addActionError("处理动作失败!"); 23. } 24. else 25. { 26. this.addActionMessage("提交成功"); 27. } 28. } 29. public String getMsg() 30. { 31. return msg; 32. } 33. public void setMsg(String msg) 34. { 35. this.msg = msg; 36. } 37. }
大家从上面的代码可以看出,Field错误需要一个key(一般用来表示是哪一个属性出的错误),而Action错误和Action消息只要提供一个信息字符串就可以了。
最后来配置一下这个Action,代码如下:
1. < package name="demo" extends="struts-default"> 2. < action name="validate" class="action.ValidateAction"> 3. < result name="success">/error/validate.jsp< /result> 4. < result name="input">/error/validate.jsp< /result> 5. < /action>
6. < /package>
假设应用程序的上下文路径为demo,则可通过如下的URL来测试程序:
http://localhost:8080/demo/validate.jsp
我们还可以使用ValidationAware接口的其他方法(由ValidationAwareSupport类实现)获得或设置字段错误信息、动作错误信息以及动作消息。如hasActionErrors方法判断是否存在动作层的错误,getFieldErrors获得字段错误信息(一个Map 对象)。下面是ValidationAware接口提供的所有的方法:
1. package com.opensymphony.xwork2;
2. 3. import java.util.Collection; 4. import java.util.Map; 5.
6. public interface ValidationAware 7. { 8. void setActionErrors(Collection errorMessages); 9. Collection getActionErrors();
10. 11. void setActionMessages(Collection messages); 12. Collection getActionMessages(); 13. void setFieldErrors(Map errorMap); 14. Map getFieldErrors(); 15. void addActionError(String anErrorMessage); 16. void addActionMessage(String aMessage); 17. void addFieldError(String fieldName, String errorMessage); 18. boolean hasActionErrors(); 19. boolean hasActionMessages(); 20. boolean hasErrors(); 21. boolean hasFieldErrors(); 22. }
=========================== 实现com.opensymphony.xwork2.TextProvider接口 --需要导入xwork.jar 该接口主要提供读取消息资源文件,获取消息文本的功能。该接口的getText()用来获取消息文本的内容。
一般继承ActionSupport助手类,其实现了TextProvider接口并提供了默认实现 所以在,action/拦截器中(只要其继承了actionSupport)要获取资料文件(properties文件),可以直接放入以下代码:
import com.opensymphony.xwork2.LocaleProvider; import com.opensymphony.xwork2.TextProvider; import com.opensymphony.xwork2.TextProviderFactory;
public class Log拦截器 extends AbstractInterceptor implements LocaleProvider { //初始化拦截器时,获取资源文件 public void init() { protected TextProvider operateCodeProvider = new TextProviderFactory().createInstance( ResourceBundle.getBundle( "LogDefinitions资源文件名", getLocale()), this); }
/** * 获取语言 */ public Locale getLocale() { ActionContext ctx = ActionContext.getContext(); if (ctx != null) { return ctx.getLocale(); } else { sysLogger.debug("Action context not initialized"); return null; } }
调用方法: this.operateCodeProvider.getText("hwj") -->便可获取hwj的值. }
看看ActionSupport 是怎样实现TextProvider的: public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable { protected static Logger LOG = LoggerFactory.getLogger(ActionSupport.class); private final transient TextProvider textProvider = new TextProviderFactory().createInstance (getClass(), this); //返回国际化信息的方法 public String getText(String aTextName) { return textProvider.getText(aTextName); } 文章出处:http://hi.baidu.com/%B1%F5%BA%A3%BB%C4%C1%D6/blog/item/35fa343ffe619bfe3c6d976e.html
posted on 2012-07-18 09:29  光辉飞翔  阅读(6304)  评论(0编辑  收藏  举报