深入分析JavaWeb Item45 -- Struts2封裝請求參數與類型轉換
作為MVC架構,必須要負責解析HTTP請求參數,並將其封裝到Model對象中,Struts2提供了非常強大的類型轉換機制用於請求資料 到 model對象的封裝。
1、Struts2 提供三種資料封裝的方式Action 本身作為model對象,通過成員setter封裝
建立獨立model對象,頁面通過ognl運算式封裝
使用ModelDriven介面,對請求資料進行封裝
1. 方式一:在動作類中成員變數給予初始值。
在設定檔中struts.xml中
/result.jsp
編寫result.jsp
${name}
編寫實作類別PersonAction,重寫excute()方法
package com.itheima.actions;import com.opensymphony.xwork2.ActionSupport;public class PersonAction extends ActionSupport { private String name = "劉小晨"; private String password; private int age; public String getName() { return name; } public void setName(String name) { System.out.println("調用了setName方法"); this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String execute() throws Exception { System.out.println(name+":"+password+":"+age); return super.execute(); }}
運行結果為:劉小晨
這種在類的成員變數時,就賦值的方法,亦可以在設定檔中注入動作類的參數值(靜態參數設定),Action 本身作為model對象,通過成員setter封裝(一個名字為params的攔截器乾的)
重新設定struts.xml如下:
王衛星 /result.jsp
運行結果為:王衛星
實際上是由一個叫做staticParams的攔截器做的,可以查看 struts-default.xml設定檔,如果將staticParams這個攔截器刪除,則得不到想要的結果
此外還有動態參數注入,同樣用動作類作為model對象。
編寫result.jsp
設定檔和動作實作類別不變,這裡要注意: 表單的欄位輸入欄位的name,password, age取值要和動作類的寫屬性名稱一致。
運行結果:表單中name輸入的結果。
動態參數的注入也是由一個攔截器來做的:params
2、方式二:動作類和模型分開(我們以表單請求參數為例)
寫設定檔struts.xml
編寫動作類StudentAction
package com.itheima.actions;import com.itheima.domain.Student;import com.opensymphony.xwork2.ActionSupport;public class StudentAction extends ActionSupport { private Student student = new Student(); public Student getStudent() { System.out.println("調用了getStudent方法"); return student; } public void setStudent(Student student) { System.out.println("調用了setStudent方法"); this.student = student; } public String execute() throws Exception { System.out.println(student); return NONE; }}
編寫模型類Student.java
package com.itheima.domain;import java.io.Serializable;public class Student implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; }}
編寫訪問的頁面addStudent.jsp
這樣上述就將模型(Student)和動作類(StudentAction)分開了,在頁面中,我們可以用student.name和student.age來封裝請求參數了。
主要原理如下:
架構建立了一個Student的執行個體,通過調用setStudent(Student s),傳遞對象架構再調用StudentAction的getStudent(),方法,得到剛剛建立的對象緊接著,調用student執行個體的setName和setAge方法,設定值。
3. 方式三:模型驅動(面試)(與後面ValueStack值棧有關)
編寫struts.xml設定檔
編寫CustomerAction的動作類
package com.itheima.actions;import com.itheima.domain.Customer;import com.opensymphony.xwork2.ActionSupport;import com.opensymphony.xwork2.ModelDriven;//使用模型驅動:public class CustomerAction extends ActionSupport implements ModelDriven{ private Customer customer = new Customer();//這裡一定要new()出一個實體類。 public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } @Override public String execute() throws Exception { System.out.println(customer); return NONE; } public Customer getModel() { return customer; }}
編寫實體類Customer.java
package com.itheima.domain;import java.io.Serializable;public class Customer implements Serializable { private String name; private String city; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Customer [name=" + name + ", city=" + city + "]"; }}
編寫addCustomer.jsp頁面
這裡可以直接寫name和city來封裝請求參數,而不需要些customer.name和customer.city。
註:模型驅動實際上是由一個攔截器來做的。modelDriven攔截器來做。把getModel方法返回的對象,壓入一個叫做ValueStack的棧頂。struts架構就會根據表單的name屬性,調用對應棧頂對象的setter方法,從而把請求參數封裝進來。可以看源碼:
注意這一定要new出來! 否則架構調用CustomerAction的getCustomer()方法,得到傳回值為null。
2、 封裝資料到集合類型中
Struts2 還允許填充 Collection 裡的對象, 這常見於需要快速錄入批量資料的場合
類型轉換與Collection配合使用
類型轉換與Map配合使用
執行個體:
編寫配置struts.xml
編寫動作類CollectionAction
package com.itheima.actions;import java.util.Arrays;import java.util.Collection;import java.util.Map;import com.itheima.domain.Employee;import com.opensymphony.xwork2.ActionSupport;public class CollectionAction extends ActionSupport { private String[] hobby;//數組 private Collection employees;//集合 private Map emps;//map public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } public Collection getEmployees() { return employees; } public void setEmployees(Collection employees) { this.employees = employees; } public Map getEmps() { return emps; } public void setEmps(Map emps) { this.emps = emps; } public String execute() throws Exception {// System.out.println(Arrays.asList(hobby));// System.out.println(employees); if(emps!=null){ for(Map.Entry me:emps.entrySet()){ System.out.println(me.getKey()+":"+me.getValue()); } } return NONE; }}
編寫實體類Employee
package com.itheima.domain;import java.io.Serializable;public class Employee implements Serializable { private String name; private float salary; public String getName() { return name; } public void setName(String name) { this.name = name; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } @Override public String toString() { return "Employee [name=" + name + ", salary=" + salary + "]"; }}
編寫collectionDemo.jsp
大量新增員工資訊
向Map中新增內容3、類型轉換
對於大部分常用類型,開發人員根本無需建立自己的轉換器,Struts2內建了常見資料類型多種轉換器,8大基本類型自動轉換。
- boolean 和 Boolean
- char和 Character
- int 和 Integer
- long 和 Long
- float 和 Float
- double 和 Double
- Date 可以接收 yyyy-MM-dd格式字串,java.util.Date<——–>String(中國:Struts2預設按照yyyy-MM-dd本地格式進行自動轉換)
- 數組 可以將多個同名參數,轉換到數組中
- 集合 支援將資料儲存到 List 或者 Map 集合
總結:在使用Struts2時,基本上不用寫任何類型轉換器。內建的完全夠用。
但是因為使用者介面傳來的資料都是String:String—->其他類型,當一些需求是顯示或者是資料回顯:其他類型—–>String時,我們就要做自訂類型轉換了。
可以看一下源碼<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="http://www.bkjia.com/uploads/allimg/160110/042R5A51-7.png" title="\" />
從源碼可以看出,繼承org.apache.struts2.util.StrutsTypeConverter(最簡單和方便)
1、自訂類型轉換器
String—————–>java.util.Date MM/dd/yyyy—–>能轉換
java.util.Date———>String MM/dd/yyyy
步驟:
1. 編寫一個類直接或間接實現:
com.opensymphony.xwork2.conversion.TypeConverter介面。
也可以選擇繼承:
com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter
我們選擇繼承org.apache.struts2.util.StrutsTypeConverter,並覆蓋掉如下方法
public Object convertValue(Object value, Class toType)
假如在動作類HelloWorldAction 中有一個時間參數createtime
import java.util.Date;public class HelloWorldAction { private Date createtime; public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; }}
自訂我們的轉換器MyDateConvertor ,覆蓋convertFromString()和convertToString()方法。
package com.itheima.convertor;import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Map;import org.apache.struts2.util.StrutsTypeConverter;//日期轉換器:/* * String :12/31/2001 ---->Date * Date---------->String:12/31/2001 */public class MyDateConvertor extends StrutsTypeConverter { private DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); //從字串轉換成日期 public Object convertFromString(Map context, String[] values, Class toClass) { if(toClass==Date.class){ String value = values[0];//擷取使用者輸入的參數值12/31/2001 try { return df.parse(value); } catch (ParseException e) { e.printStackTrace(); } } return null; } //日期轉換成字串 public String convertToString(Map context, Object o) { if(o instanceof Date){ Date d = (Date)o; return df.format(d); } return null; }}
2.註冊類型轉換器
局部類型轉換器:為某個動作類服務的(特服)
在Action類所在的包下放置ActionClassName-conversion.properties檔案,ActionClassName是Action的類名,後面的-conversion.properties是固定寫法,對於本例而言,檔案的名稱應為HelloWorldAction-conversion.properties 。在properties檔案中的內容為:
屬性名稱=類型轉換器的全類名
對於本例而言, HelloWorldAction-conversion.properties檔案中的內容為:
createtime= cn.itcast.conversion.DateConverter
全域類型轉換器:為所有的動作類服務的
在WEB-INF\classes目錄下(在src下)建立一個固定名稱的設定檔:xwork-conversion.properties
對於本例而言, xwork-conversion.properties檔案中的內容為:
java.util.Date= cn.itcast.conversion.DateConverter
3、出現轉換失敗時的頁面轉向和錯誤訊息提示
Struts2提供了一個名為conversionError的攔截器,查看struts-default.xml
如果Struts2的類型轉換器執行類型轉換時出現錯誤,該攔截器將負責將對應錯誤封裝成表單域錯誤(FieldError),並將這些錯誤資訊放入ActionContext中
使用類型轉換中的錯誤處理使用者定義Action必須繼承ActionSupport
在自訂類型轉換器中,異常必須拋出不能捕獲,conversionError會處理該異常,然後轉入名為input的邏輯視圖
在Action所在包中,建立 ActionName.properties,在局部資源檔中配置提示資訊 : invalid.fieldvalue.屬性名稱= 錯誤資訊
在input邏輯視圖所對應jsp頁面中,通過 輸出類型轉換資訊
a、配置一個name=”input”的結果檢視,一般指向使用者輸入資料的那個頁面
b、在sp中使用struts2的標籤顯示欄位有關的錯誤(後面的國際化再講)
<%@ taglib="" uri="/struts-tags" prefix="s">
<%@ taglib="" uri="/struts-tags" prefix="s">
c、配置提示資訊為中文的
在動作類所在的包中,建立一個名稱為:動作類名.properties的設定檔