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