少爺,今天我們開始修煉struts2的參數(shù)封裝。首先要知道參數(shù)有靜態(tài)參數(shù)和動態(tài)參數(shù)之分。
那么我們就先修煉靜態(tài)參數(shù)的封裝吧。
“好的福伯,我一定好好去學的,長大也想成為父親那樣偉大的代碼英雄”
好,好。少爺,你要努力啦,我們開始修煉。
1.靜態(tài)參數(shù)的封裝
在struts.xml中使用依賴注入的方式對動作類參數(shù)賦值(動作類中set.get方法)
<param name="username">Went_Gone</param>
<param name="age">18</param>
2.動態(tài)參數(shù)的封裝
少爺,動態(tài)參數(shù)是JavaWeb開發(fā)中必須要用到的。那就讓我們先了解一下動態(tài)參數(shù),什么是動態(tài)參數(shù)呢,就是可以進行動態(tài)變化的不像上邊那樣在struts中寫死的數(shù)據(jù)。而動態(tài)參數(shù)的封裝有三種方式,哪三種呢~
2.1 數(shù)據(jù)模型和動作類寫在一起(即動作類中有數(shù)據(jù)模型)
例如:
public class Demo2Action extends ActionSupport {
//數(shù)據(jù)模型
private String username;
private int age;
public String addUser(){
System.out.println(username+","+age);
return null;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
使用此種封裝方式,那么你在傳遞參數(shù)的時候需要表單中的name屬性的取值必須和動作模型中set方法后邊的名稱一致。
如圖:

此方式簡單易懂,但是在實際開發(fā)中,用的不是很多,局限性大,并且數(shù)據(jù)模型和動作類寫在一個類中,不利于解耦管理。由此出現(xiàn)了動態(tài)參數(shù)封裝的第二種方式。
2.2 數(shù)據(jù)模型和動作類分別寫在不同類中(即數(shù)據(jù)模型建立javabean)
例如:
動作類:
public class Demo3Action extends ActionSupport {
//動作類中只有一個javabean對象。通過set.get方法進行管理
private User user;
public String addUser(){
System.out.println(user.getUsername()+","+user.getAge());
return null;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
所需要的數(shù)據(jù)模型:
public class User implements Serializable{
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
但是使用這個方法要注意:
表單中的name屬性值必須是javabean的對象的屬性
- 不是簡單的username了,變成了user.username
還有動作類中set.get方法的執(zhí)行順序:
get方法:struts2先調(diào)用get方法,判斷對象是否存在,如果不存在,使用反射創(chuàng)建一個對象。
set方法:不存在的時候,創(chuàng)建一個對象,把它set進去
get方法:再調(diào)用get方法得到對象,調(diào)用set方法得到對象的各種屬性,為屬性賦值
2.3使用數(shù)據(jù)模型驅(qū)動進行參數(shù)封裝。
使用模型驅(qū)動進行參數(shù)封裝時,數(shù)據(jù)模型必須和動作類方法不在同一個類中。
使用步驟如下:
1. 動作類實現(xiàn)ModelDriven的接口
2. 實現(xiàn)接口中的抽象方法getModel
3. 使用模型驅(qū)動時,數(shù)據(jù)模型必須由我們自己實例化。即
private User user = new User();
此時在form表單中可以直接使用username進行參數(shù)傳遞了~
如圖:

少爺,這一塊能消化的了么。
“福伯,還好啦,我再做個demo練習一下就好啦~”
完成參數(shù)封裝后,再做項目的時候經(jīng)常會遇到用戶不按套路出牌的情況,比如填寫時間的時候2017-04-20,用戶非要寫成04/20/2017形式,如果我們不進行轉(zhuǎn)化,就會出現(xiàn)數(shù)據(jù)庫無法保存甚至程序崩潰的情況。那么接下來我們就開始修煉數(shù)據(jù)類型的轉(zhuǎn)化~
3.數(shù)據(jù)類型轉(zhuǎn)換
struts2提供常用的類型轉(zhuǎn)換
1.基本數(shù)據(jù)類型的自動轉(zhuǎn)換 比如int
2.日期類型,默認按照本地日期格式轉(zhuǎn)換(yyyy-MM-dd)
3.字符串數(shù)組類型,默認用逗號+空格(", "),連接成一個字符串
比如上邊提到的年月日的格式情況,這就需要少爺進行自定義類型轉(zhuǎn)換器。那么如果進行自定義呢,哈哈,像struts2這么強大的工具怎么能沒有數(shù)據(jù)轉(zhuǎn)換的工具類呢。那么我們就以日期類型進行小型修煉。準備好了么,來嘍
struts2中有一個StrutsTypeConverter的抽象類,我們通過繼承此類,實現(xiàn)其中的convertFromString,convertToString這兩個方法進行數(shù)據(jù)類型的轉(zhuǎn)換。
/**
* 需求:
* 把表單中MM/dd/yyyy的數(shù)據(jù)轉(zhuǎn)成日期類型
* 把數(shù)據(jù)庫中的本地日期格式轉(zhuǎn)成MM/dd/yyyy形式輸出
*
* 自定義類型轉(zhuǎn)化器
* 繼承StrutsTypeConverter,實現(xiàn)方法
* @author asmin
*
*/
public class DateTypeConvertor extends StrutsTypeConverter {
//定義一個類型轉(zhuǎn)換器
private DateFormat format = new SimpleDateFormat("MM/dd/yyyy");
/**
* 把字符串數(shù)組數(shù)據(jù)中的數(shù)據(jù)轉(zhuǎn)成日期類型
* 參數(shù):
* Map context:是OGNL的上下文對象,暫時不用
* String[] values:要轉(zhuǎn)換的數(shù)據(jù)
* Class toclass:目標類型
*/
public Object convertFromString(Map context, String[] values, Class toClass) {
//1.檢查有無數(shù)據(jù)
if(values==null || values.length == 0){
return null;
}
//2.取出數(shù)組中的第一個元素
String date = values[0];
//3.判斷目標類型的字節(jié)碼是不是日期類型
if(toClass == java.util.Date.class){
//4.使用DateFormat進行轉(zhuǎn)化,并且返回轉(zhuǎn)換后的結(jié)果
try {
return format.parse(date);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
return null;
}
/**
* 把日期類型的數(shù)據(jù)轉(zhuǎn)成字符串
* 參數(shù):
* Map context:是OGNL的上下文對象,暫時不用
* Object o:要轉(zhuǎn)換的數(shù)據(jù)
*/
public String convertToString(Map context, Object o) {
//1.判斷object是不是日期類型
if(o instanceof Date){
Date date = (Date)o;
//2.如果是日期類型,使用轉(zhuǎn)換器轉(zhuǎn)換成指定格式的字符串,并返回
return format.format(date);
}
return null;
}
}
“哈哈,我也會自定義數(shù)據(jù)轉(zhuǎn)換類型了,太好了~”
少爺,不要高興的太早,這僅僅是寫好轉(zhuǎn)換類了,真正起作用的是你要在哪使用,怎么去使用。
“啊,還有啊,好吧,我以為這就可以了呢”
接下來就是重點難點了,少爺你可看清楚了。
配置分為局部類型轉(zhuǎn)換器與全局類型轉(zhuǎn)換器,我們先說局部類型轉(zhuǎn)換器
3.1局部類型轉(zhuǎn)換器
要在所要使用的實體類(即javaBean)同包下建一個properties。命名規(guī)則:javabean的名稱-conversion.properties ,它的內(nèi)容呢就是鍵值對的形式存在的,你所使用的屬性名作為key,而轉(zhuǎn)換器的全類名作為value,如圖:

3.2全局類型轉(zhuǎn)換器
少爺,考你一下,局部類型轉(zhuǎn)換器配置文件放置在所使用的的javabean的包下,那全局的應該放在哪呢?
“我想想,恩...,局部放在里邊,那全局就放在外邊唄,放在類路徑的根路徑?!?/p>
是的,很聰明啊,全局類型轉(zhuǎn)換器就是放在類路徑的根路徑,但是注意,他的命名規(guī)則是固定的,寫死的“xwork-conversion.properties”,它同樣是鍵值對的形式存在的,這時候你不能使用屬性名了,因為你不可能在項目中將所有的要使用該轉(zhuǎn)換器的屬性都叫成一個名字,所以這里要用使用的數(shù)據(jù)類型(全類名)作為key,而轉(zhuǎn)換器的全類名作為value,如圖:

少爺,還要記住一點,我們在開發(fā)過程中,推薦全局類型轉(zhuǎn)換器。
“好的,福伯,我記住了!”
最終運行結(jié)果:

少爺,你覺得有了類型轉(zhuǎn)換器之后是不是開發(fā)方便了很多啊。
“是的福伯,簡便快捷了,這樣我就不用為了數(shù)據(jù)類型發(fā)愁了~”
錯了少爺,我們不可能在一個項目中將所有的數(shù)據(jù)類型轉(zhuǎn)換器寫的那么完善的,所以我們還要解決當數(shù)據(jù)類型轉(zhuǎn)換出異常時我們的解決方法。記住,我們給用戶的永遠不能是Struts Problem Report...
3.4數(shù)據(jù)類型轉(zhuǎn)換失敗后的處理
這時候我們要定義一個input的結(jié)果視圖進行回到原來的視圖結(jié)果。
使用struts2的標簽庫:
- 首先導入struts2的標簽庫。
<%@taglib uri="/struts-tags" prefix="s" %>
- 使用struts2的標簽進行標記錯誤字段及動作類錯誤。
<s:actionerror/><%-- 動作錯誤 --%>
<s:fielderror/><%-- 字段錯誤 --%>
- 使用struts2的標簽進行文本框及form表單的替換
<%-- struts2的form標簽,它提供了和原始html表單標簽幾乎一致的屬性
action:請求地址,可以直接寫動作名稱,不用寫前邊的contextPath
method:請求方式,在這里不需要寫,struts2的form表單默認是post
enctype:表單編碼的MIME類型
--%>
<s:form action="register">
<s:textfield name="username" label="用戶名"/>
<s:password name="password" label="密碼"/>
<s:textfield name="birthday" label="生日"/>
<s:checkboxlist name="hobby" label="愛好" list="{'吃飯','籃球','代碼'}"/>
<s:radio list="#{true:'已婚',false:'未婚'}" name="married" value="true" lable="婚否"/>
<s:submit value="注冊"/>
</s:form>
4.數(shù)據(jù)驗證
數(shù)據(jù)的校驗在開發(fā)中是由服務器+客戶端共同完成的,這樣可以減少客戶端訪問服務器的次數(shù)。但是不論怎樣,服務器的校驗是絕對不能少的,這個必須要銘記在心啊少爺。
“福伯。我知道了客戶端可以通過檢驗是否是null進行判斷,那服務器怎么判斷呢,難道也是根據(jù)null或者""來判斷么?”
哈哈,自有方法。
我們可以去看一下struts2的源碼,會發(fā)現(xiàn)里邊提供了一個Map用來建立錯誤提示。那么讓我們看看該怎樣用呢~
兩種用法哦~:
4.1 編程式驗證
什么叫編程式驗證呢,就是重新struts2的驗證方法validate。通過struts2的long包,進行判斷是否是null和"",然后通過addFiledError("字段名","錯誤信息");來進行存儲某個字段的額錯誤信息。
/**
* 在struts2的框架中,也提供了一個Map<表單的字段名,錯誤提示>
* 我們要做的:
* 往Map中存放錯誤信息提示
*
* 編程式驗證:
* 1.動作類必須繼承ActionSupport
* 2.重寫validate方法
*
* validate方法在動作方法執(zhí)行之前進行驗證
*
* 當重寫validate方法,他會對動作類中的所有的動作方法進行驗證。
* @return
*/
public void validate(){
if(StringUtils.isBlank(user.getUsername())){
//存入錯誤信息,直接調(diào)用父類的addFieldError方法,存入錯誤信息,第一個參數(shù)是表單name屬性的值,第二個參數(shù)是錯誤提示。
addFieldError("username","請輸入用戶名");
}
}
代碼中也有提到,這種編程式驗證會對所有的動作方法進行驗證,那當我們不想對某個方法進行驗證,但是其他動作方法都需要進行驗證該怎么辦呢,少爺,可以使用以下兩種跳過驗證的方法:
第一種方式:在想要跳過的動作方法前使用@SkipValidation
@SkipValidation
public String findAll(){
return SUCCESS;
}
第二種方式:定義驗證方法的名稱:validate+動作名稱注意大小寫問題 此時去掉validate方法
public String validateAddUser(){
}
但是編程式驗證的弊端也顯現(xiàn)出來了,硬編碼,不靈活。
4.2 聲明式驗證
少爺,這個聲明式驗證是在開發(fā)中經(jīng)常被用到的驗證方式,因為需要驗證誰就寫誰,簡單方便。
通過編寫驗證規(guī)則的xml文件,需要驗證時編寫xml文件,不需要時不編寫。
解決了編程式編碼的弊端。
1.針對動作類中所有動作進行驗證,在動作類所在的包下,建立一個ActionClassName-validation.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<!-- 當使用ActionClassName-validation.xml命名文件時,它是一個動作類(所有動作方法)的驗證器。 -->
<validators>
<!-- 基于字段的聲明式驗證 -->
<!-- field中name屬性指定的是表單中name屬性的取值 -->
<field name="username">
<!-- struts2的框架集成了很多內(nèi)置驗證器 requiredstring會驗證輸入內(nèi)容是否為空、空字符串,去左右空格 -->
<field-validator type="requiredstring">
<message>用戶名不能是空白~</message>
</field-validator>
</field>
</validators>
少爺,我在上邊寫了一個注釋,基于字段的聲明驗證,你知道是為什么嗎?
“唔···,還有其他的驗證方式,但是我不知道還有什么樣的驗證方式”
呵呵,有長進。
還有一種是叫做基于驗證器的驗證方式
<!-- 基于驗證器驗證 -->
<validator type="requiredstring">
<!-- 以注入的方式,提供要驗證的字段的信息
setFieldName("password");
-->
<param name="fieldName">password</param>
<message>密碼不能為空~</message>
</validator>
同樣可使用SkipValidation進行跳過驗證,這可是通用的。
2.針對動作類中的某個動作進行驗證:在動作類所在的包下,建立一個ActionClassName-ActionName-validation.xml,這里的ActionName不是動作方法名稱,是struts.xml中的對應的方法名
少爺,容屬下展示下兩種方式的示意圖:

少爺,這段時間您修煉成功了動態(tài)參數(shù)封裝和驗證器的使用手冊,可喜可賀啊。
“是啊,不過這才剛開始呢,不說了,靈兒還等著我一塊逛街去呢,走啦,福伯~,明天我會準時來修煉的?!?/p>