struts2的参数传递和OGNL息息相关,先从几个OGNL的案例来感受一下
OGNL的概念和作用,不赘述,直接百科传送门:
http://baike.baidu.com/view/1347280.htm。使用OGNL可以极为灵活的在页面上对目标对象进行存取操作,而不仅仅是只是数据的展示。下面使用OGNL给出几个常用的例子。
测试用实体类User和Department
1 package demo.ognlDemo;
2
3 public class User {
4
5 private String name;
6 private int age;
7 private Department department;
8
9 public void hello(String name) {
10 System.out.println("hello " + name);
11 }
12
13 public static void sayHi() {
14 System.out.println("HI");
15 }
16
17 public User() {
18 super();
19 }
20
21 public User(String name, int age, Department department) {
22 super();
23 this.name = name;
24 this.age = age;
25 this.department = department;
26 }
27 //get/set方法略
28} 1 package demo.ognlDemo;
2
3 public class Department {
4
5 private String name;
6 private int no;
7 下面给出测试类
1 package demo.ognlDemo;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import ognl.Ognl;
9 import ognl.OgnlException;
10
11 import org.junit.Test;
12
13
14 public class OgnlDemo {
15
16 @Test
17 public void test01() {
18
19 //定义两个演示用的对象
20 User u = new User();
21 u.setAge(20);
22 u.setName("Tom");
23
24 Department dep = new Department();
25 dep.setName("Security");
26 dep.setNo(10);
27
28 u.setDepartment(dep);
29
30 //一个根map
31 Map<String , Object> ctx = new HashMap<String, Object>();
32 ctx.put("user", u);
33 ctx.put("department", dep);
34
35 try {
36 //下列表达式都是在以指定对象为根进行属性查找
37 //以u为根
38 System.out.println(Ognl.getValue("name", u));
39 System.out.println(Ognl.getValue("age", u));
40 System.out.println(Ognl.getValue("department.name", u));
41 System.out.println("====================================");
42 //以dep为根
43 System.out.println(Ognl.getValue("name", dep));
44 System.out.println(Ognl.getValue("no", dep));
45 System.out.println("====================================");
46
47 //以集合为根
48 System.out.println(Ognl.getValue("department.name", ctx,ctx));
49 //从根的集合中取值需要添加#来区分
50 System.out.println(Ognl.getValue("#department.name", ctx,u));
51 //访问对象的方法
52 System.out.println(Ognl.getValue("hello('Jerry')",ctx,u));
53 //访问静态的方法,格式为@XXX.XXX.XXX(包名...类名)@XXX(静态方法名)
54 System.out.println(Ognl.getValue("@demo.ognlDemo.User@sayHi()",u));
55 //root是根元素的键值,通过#root可以直接取得根元素
56 System.out.println(Ognl.getValue("#root.name", ctx,u));
57 System.out.println("====================================");
58
59 //以一个集合为根,取得其中的某个元素
60 List<User> users = new ArrayList<User>();
61 User u1 = new User();
62 u1.setName("zhangsan");
63 User u2 = new User();
64 u2.setName("lisi");
65 users.add(u1);
66 users.add(u2);
67 System.out.println(Ognl.getValue("#root[0].name",users));
68
69 //或者
70 System.out.println(Ognl.getValue("get(0).name",users));
71
72 } catch (OgnlException e) {
73 e.printStackTrace();
74 }
75 }
76 }
下面回到struts2,struts2中的所有参数都保存在ValueStack中,而ValueStack就是一个OGNL结构。ValueStack由两部分组成,一个是ActionContext,类似于OGNL的Map集合,另一个是compoundRoot,类似于OGNL的根对象,使用ArrayList维护了一个栈结构。而参数则可以通过三种方式进行传递。
方法一:在action中添加属性,并为属性添加get/set方法
这个方法实际上就是在ValueStack的根中创建一个action对象,取值的时候如果没有特别指定,则默认取得栈顶的对象。在取值的时候从栈顶开始依次向下获取,根据Key值返回第一个取得的值,同时,我们也可以对ValueStack进行操作,使用ActionContext.getContext().getValueStack()获得ValueStack,进行push,peek或者remove等操作
方法二:使用ActionContext存放值
方法三:使用ServletActionContext存放值
Action:
1 package demo.ognlDemo;
2
3 import org.apache.struts2.ServletActionContext;
4
5 import com.opensymphony.xwork2.ActionContext;
6
7 public class DemoAction {
8 //1:通过action属性传值
9 private String name;
10 private String age;
11
12 public String execute(){
13
14 name = "Tom";
15 age = "99";
16
17 //2:通过actioncontext传值
18 ActionContext.getContext().put("demokey", 99999999);
19
20 //3:通过servletActionContext进行传值
21 ServletActionContext.getRequest().setAttribute("servletkey", 00000000);
22
23 return "success";
24 }
25 //get/set方法略
26
27 }
JSP:
1 <%@ page language="java" contentType="text/html; charset=UTF-8" isELIgnored="false"
2 pageEncoding="UTF-8"%>
3 <%@taglib prefix="s" uri="/struts-tags" %>
4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
5 <html>
6 <head>
7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
8 <title>struts2 Demo</title>
9 </head>
10 <body>
11
12 对于EL表达式而言,不论值存放在什么地方,都可以取得,但是使用struts2的标签,需要注意key值的写法
13
14 <hr>
15 通过属性进行传值:<br>
16 EL表达式 -->${name}:${age}<br>
17 s:property标签 --<s:property value="name"/> : <s:property value="age"/>
18 <hr>
19
20 通过actionContext进行传值<br>
21 EL表达式--> ${demokey}<br>
22 s:property标签(访问actionContext中的数据,需要在键值之前添加“#”) --<s:property value="#demokey"/>
23 <hr>
24
25 通过servletActionContext进行传值<br>
26 EL表达式 -->${servletkey}<br>
27 s:property标签 (访问servletActionContext中的数据,需要在键值之前添加“#”,并且需要注明范围:request/session/application)--<s:property value="#request.servletkey"/>
28 <hr>
29 </body>
30 </html>
为了能查看更详细的信息,开发过程中可以在页面中加入调试标签
1 <s:debug/>
标签展开之后Value Stack Contents就是根的内容,Stack Context就是集合的内容
上述内容是从action到jsp的参数传递,下面是从jsp到action的参数传递
方法一:通过地址栏参数进行参数传递,/XXX?param=YYY,在Action里可以直接通过对应的属性来获取,或者直接通过
servletActionContext取得request,获取参数值。
这种方法对于少量的参数传递来说没有问题,但是碰到大量的参数或者中文,其他特殊参数的传递就显得力不从心,可能需要经过转码进行传递,不方便。
方法二:表单传递参数,较直接使用地址栏传参安全一些,但是参数一多,仍然很头疼。可以使用以下方法将需要获取的值注入到指定的对象中。
1:比如,在通常情况下,为了传递多个值到一个user对象中,我们可以将表单域的名字写为
1 <input name="user.id" id="uid"/>
这样一来,user的id属性就会接收到相应的值,以此类推,user的其他属性值都可以参照此写法。表单提交之后,在action中就可以使用user进行操作。
2:或者我们可以使action实现ModelDriven<T>接口,这样一来在传递表单的时候,表单域的name属性就无需指定要传递到的对象,直接写作
1 <input name="id" id="uid"/>
ModelDriven实际上就是在ValueStack中,root里的action之上又添加了一个指定的对象,该对象就是实现接口时指定的T,提交表单的时候,参数的值会根据root中的对象,从上到下依次填充,表单域中的属性首先填充到ModelDrive的对象。
上面提到的这两种值填充,在某些情况下可能会有问题,要填充的属性若是非String类型,比如Date类型,这样就无法直接转换,但是一般来说前端会使用控件来限制日期格式的输入,然后通过String来接收日期字符串,自行转换。struts2提供了这种问题的解决方案,虽然用的不多,但是见仁见智,转换器有时也有它的用武之地。
要使用struts2的转换器,需要新建类,然后继承StrutsTypeConverter
1 package demo.ognlDemo;
2
3 import java.text.ParseException;
4 import java.text.SimpleDateFormat;
5 import java.util.Date;
6 import java.util.Map;
7
8 import org.apache.struts2.util.StrutsTypeConverter;
9 /**
10 * 自定义的局部转换器,将时间和字符串进行转换
11 * @author Administrator
12 *
13 */
14 public class DateConverter extends StrutsTypeConverter{
15
16 SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
17
18 /**
19 * 字符串到对象
20 */
21 @SuppressWarnings("rawtypes")
22 @Override
23 public Object convertFromString(Map context, String[] values, Class toClass) {
24
25 try {
26 if (values.length > 0 && values.length <= 1) {
27 String str = values[0];
28 return sdf.parse(str);
29 }
30 return null;
31 } catch (ParseException e) {
32 e.printStackTrace();
33 return null;
34 }
35
36
37 }
38
39 /**
40 * 对象到字符串
41 */
42 @SuppressWarnings("rawtypes")
43 @Override
44 public String convertToString(Map context, Object o) {
45 Date d = (Date)o;
46 return d.toString();
47 }
48
49 }
50
给出一个字符串和日期相互转换的例子,重写父类的
convertFromString和
convertToString方法,
convertFromString意味着吧页面上传递过来的String参数经过加工变成自己想要的参数类型,
values是参数数组,一般只有一个。convertToString意味着将参数从action传出到页面的时候需要进行的转换。写好转换器之后,就需要配置自定义转换器的使用,转换器有局部和全局之分,两种都需要定义对应的properties文件。
局部转换器,也就是针对某个action用的转换器,需要定义名为“action名称-conversion.properties”的配置文件,并且该文件需要和对应的action置于同一目录,里面配置哪些属性需要用哪个转换器转换。这段配置就意味着对应action中的creDate(Date类型)的属性,使用
DateConverter转换1 creDate = demo.ognlDemo.DateConverter
全局转换器不限于哪个action使用,对整个项目都生效,需要配置名为“xwork-conversion.properties”的配置文件,置于src目录下,里面需要配置具体action的具体哪个属性用哪个转换器转换
1 demo.ognlDemo.XXXAction.creDate = demo.ognlDemo.DateConverter