Struts2淺析 之 模型驅動與屬性驅動
1.Struts2的屬性驅動.
Struts2的屬性驅動指的是在action中JSP頁面的每一個form中的name都對應在action中有一個屬性與之對應。看下面程式碼片段:
<form action="register.do" name="RegisterForm" method="post"> Username:<input type="text" name="uname"><br> Password:<input type="password" name="upass"><br> Confirm: <input type="password" name="rpass"><br> <input type="submit" value="Submit"> </form>
這是一個最基本的使用者註冊的表單,它有三個資料要提交: uname、upass、rpass,那麼對應的Action也要有三個屬性(其實是三個屬性的setter),看register.do實際的Action定義類:
你會看到RegisterAction中的三個屬性和RegisterForm表單中的name屬性名字一模一樣,沒錯,這就是Struts2的屬性驅動,當表單提交到RegisterAction後,Struts2將會自動將根據表單的name屬性呼叫Action中相應的屬性setter,去自動賦值。package org.abu.csdn.action.user; import com.opensymphony.xwork2.ActionSupport; /** * Struts2屬性驅動演示用的Action */ public class RegisterAction extends ActionSupport { private String uname; private String upass; private String rpass; public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public String getUpass() { return upass; } public void setUpass(String upass) { this.upass = upass; } public String getRpass() { return rpass; } public void setRpass(String rpass) { this.rpass = rpass; } @Override public String execute() throws Exception { return ActionSupport.SUCCESS; } }
2.Struts2的模型驅動
Struts2的模型驅動其實和Struts1.x中的ActionForm有點類似,在Struts1.x中每一個Action都必須有一個ActionForm與之對應,而Struts2.0中,每一個Action同樣需要提供一個POJO物件,用來封裝表單屬性,看程式碼:
這段表單的程式碼和上面的一模一樣,就不贅述了。接下來看POJO的程式碼,其實就是普通的Java Bean:<form action="register.do" name="RegisterForm" method="post"> Username:<input type="text" name="uname"><br> Password:<input type="password" name="upass"><br> Confirm: <input type="password" name="rpass"><br> <input type="submit" value="Submit"> </form>
package org.abu.csdn.action.user;
import com.opensymphony.xwork2.ActionSupport;
/**
* Struts2屬性驅動演示用的Java Bean
*/
public class User {
private String uname;
private String upass;
private String rpass;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpass() {
return upass;
}
public void setUpass(String upass) {
this.upass = upass;
}
public String getRpass() {
return rpass;
}
public void setRpass(String rpass) {
this.rpass = rpass;
}
}
也是和屬性驅動中的例子一樣,很簡單,因為演示的都是同一個例子,只是方法不同而已,但是接下來就不一樣了,看RegisterAction程式碼:
package org.abu.csdn.action.user;
import org.abu.csdn.dto.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
/**
* Struts2模型驅動演示用的Action
*
*/
public class RegisterAction extends ActionSupport implements ModelDriven<User> {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
return ActionSupport.SUCCESS;
}
// 模型驅動必須實現的方法,也是ModelDriven介面中唯一的方法
public User getModel() {
return user;
}
}
和屬性驅動的Action有很大的區別,下面一一列舉:
(1)模型驅動的Action必須實現ModelDriven介面,而且要提供相應的泛型,這裡當然就是具體使用的Java Bean了。
(2)實現ModelDriven的getModel方法,其實就是簡單的返回泛型的一個物件。
(3)在Action提供一個泛型的私有物件,這裡就是定義一個User的user物件,並提供相應的getter與setter。
好了,上面的三件事做完之後,Action就會去自動呼叫User的setter將表單中的name屬性的值賦給User中的屬性。而Action的後續處理的Jsp頁面後者是Servlet就可以使用user物件了。
3.到底是用屬性驅動和是模型驅動呢?
這個問題困擾了很多Struts2的初學者,我這裡提供一些建議:
(1)請你統一整個系統中的Action使用的驅動模型,即要麼都是用屬性驅動,要麼都是用模型驅動。
(2)如果你的DB中的持久層的物件與表單中的屬性都是一一對應的話,那麼就使用模型驅動吧,畢竟看起來程式碼要整潔得多。
(3)如果表單的屬性不是一一對應的話,那麼就應該使用屬性驅動,否則,你的系統就必須提供兩個Bean,一個對應表單提交的資料,另一個用與持久層。
看上面的例子,其實密碼確認rpass是不需要放到DB中去的,而僅僅是用於校驗密碼的,不是嗎?那麼如果使用模型驅動的話,就存在這個問題了,而使用屬性驅動的話又有些繁瑣,現在我們就來調整一個看我的解決方法。
4.完整的例子
(1)表單提交的JSP頁面index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>CSDN首頁</title>
</head>
<body>
<s:a href="userManager/userRegister.jsp">使用者註冊</s:a>
<form action="register.do" name="SampleForm" method="post">
Username:<input type="text" name="uname"><br>
Password:<input type="password" name="upass"><br>
Confirm: <input type="password" name="rpass"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
(2)User的定義
package org.abu.csdn.action.user;
import com.opensymphony.xwork2.ActionSupport;
/**
* Struts2屬性驅動演示用的Java Bean
*
*/
public class User {
private String uname;
private String upass;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpass() {
return upass;
}
public void setUpass(String upass) {
this.upass = upass;
}
}
(3)RegisterAction的定義
package org.abu.csdn.action.user;
import com.opensymphony.xwork2.ActionSupport;
/**
* Struts2屬性驅動演示用的Action
*
*/
public class RegisterAction extends ActionSupport {
// 用來給spring注入,及屬性拷貝
private User user;
private String uname;
private String upass;
private String rpass;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpass() {
return upass;
}
public void setUpass(String upass) {
this.upass = upass;
}
public String getRpass() {
return rpass;
}
public void setRpass(String rpass) {
this.rpass = rpass;
}
@Override
public String execute() throws Exception {
// 呼叫方法將屬性copy到user中去,便於後續的service方法
copyAttribute();
// TODO:編寫自己的user業務程式碼
return ActionSupport.SUCCESS;
}
/**
* 注意這是一個action全域性的校驗
*/
@Override
public void validate() {
// 進行密碼的校驗
if (!(upass.trim()).equals((rpass.trim()))) {
// 注意這裡addFieldError的fieldName引數必須用引號引起來,
// 不能這樣使用addFieldError(rpass,"XXXXXX"),而必須是addFieldError("rpass","XXXXX")
this.addFieldError("rpass", this
.getText("csdn.action.user.register.validate.verify"));
}
}
/**
* 將表單屬性中需要持久化的屬性拷貝到user中去
* @author 阿布
*
*/
private void copyAttributes () {
user.setUname(uname);
user.setUpass(upass);
}
}
(4)Struts.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="root" extends="struts-default" namespace="/userManager">
<action name="userRegister" class="org.abu.csdn.action.user.RegisterAction">
<result name="success">/index.jsp</result>
<result name="input">/userManager/userRegister.jsp</result>
</action>
</package>
</struts>
小結:
為什麼要使用屬性驅動和模型驅動
struts2與struts很大的不同點在於,struts的execute方法提供了HttpServletRequest和HttpServletResponse方法在獲取客戶端提交的資料資訊的時候需要使用HttpServletRequest的getParameter()方法,並且還需要進行必要的資料型別轉換。若客戶端提交的資料量大的時候,我們則需要寫很多的getParameter方法。這樣程式碼量就相應的增加不少。但是struts2為我們提供了屬性驅動和模型驅動,它不需要我們寫很多的獲取值的方法。而只需要我們在Action中定義相應的getter方法,在介面上以Action中的變數名作為表單元素的name屬性值即可。
struts2 一切的工作都是由struts2的引數攔截器來完成的。
屬性驅動。舉個例子,頁面上有個標籤name="user" value="admin"。最後,提交到後臺會以user=admin的形式。被攔截器獲悉後,它就會查詢action中是否有user這個引數,如果找到就呼叫setUser將admin賦給user。當請求返回的到jsp頁面時,將jsp編譯成servlet,如果頁面中有name="user"的標籤,或是直接用${user}的時候,攔截器會呼叫action中user引數的getUser方法,將user的值通過Response傳送出去。
模型驅動(我理解的模型驅動就是用攔截器傳遞一個javaBean)原理一樣。只不過需要兩次呼叫getset方法。先呼叫javaBean的再呼叫具體引數的。struts2的模型驅動要實現ModelDriver<T>這個介面,並且還必須重寫getModel()這個方法。
屬性驅動和模型驅動有什麼異同?
1.屬性驅動
對於屬性驅動,我們需要在 Action 中定義與表單元素對應的所有的屬性,因而在 Action 中會出現很多的 getter 和 setter 方法。
2.模型驅動
對於模型驅動,使用的 Action 物件需要實現ModelDriven介面並給定所需要的型別.而在 Action 中我們只需要定義一個封裝所有資料資訊的 javabean 。
3.屬性和模型驅動的相同點
當我們使用屬性驅動和模型驅動的時候,必須將表單的元素中的 name 屬性值與我們定義接收資料資訊的變數名對應起來。