[IBM
developerWorks 中國
?]
本文講述了如何利用Java的反射的機制來簡化Structs應用程序的開發。
Struts中引入ActionForm類的意義與缺陷:
在Struts應用程序中,ActionForm是一個很重要的概念,它的主要功能就是為Action的操作提供與客戶表單相映射的數據(如果在客戶指定的情況下,還包括對數據進行校驗)。Action根據業務邏輯的需要,對數據狀態進行修改,在改變系統狀態后,ActionForm則自動的回寫新的數據狀態并保持。程序員對JSP與ActionForm Bean的對應關系,通常感到很迷惑,JSP與ActionForm到底是1:1,還是N:1,對此,Struts本身對此并沒有提出自己的觀點。無論是一對一,還是多對一,Struts本身并不關心,它都能很好得工作。Struts在它的開發文檔中指出,對于較小規模的開發,開發人員可以根據自己的需要,每個模塊只寫一個ActionForm Bean,甚至整個應用程序只寫一個ActionForm Bean.當然,Struts也不反對每個ActionForm Bean只對應一個JSP,他們之間的對應關系,由開發人員自己決定。
在我看來,正如Entity EJB對J2EE的重大貢獻一樣,Entity EJB使得程序員對二維關系數據庫的存取對象化了,程序員可以使用Set 或者Get等面向對象的方法來操縱關系數據庫的數據,而ActionForm也使得程序員對網頁的數據存取奇跡般的對象化了,程序員同樣也可以使用Set 或者Get等面向對象的方法存取網頁上的數據,這是一個開發模式方式上的重大轉變。基于此,我個人認為ActionForm與JSP即VIEW層的關系最好是一對一的關系,這樣,在理解上會更清晰一些。但是,這樣也會帶來一個很現實的問題,在一個應用程序中,也許有非常多得JSP頁面,如果每個ActionForm 都只對應一個JSP頁面,那么系統的Java代碼就會急劇膨脹起來,而且,每個ActionForm都是只有很簡單的Set或者Get方法存取數據,那么,如何簡化Struts應用程序的開發呢?
在Struts1.1 中,Struts引入了DynaActionForm和Dyna Bean,試圖解決這個問題,在我看來,DynaActionForm的引入,破壞了對網頁存取對象化的概念,使開發人員重新回到了使用HashTable、Map、Collection、ArrayList等集合對象來實現對數據進行存取的老路上來。雖然應用程序的靈活性大大增加了,但是代碼的可讀性也大大降低了,開發人員之間的交流難度也增加了。
在傳統的應用程序對ActionForm Bean的訪問中,我們通常都寫成如下的形式:
Connection?conn
=
DriverManager.getConnection(
"
JDBC?URL?
"
);
??????sql
=
"
?select?*??from?some?tables?
"
;
PreparedStatement?stmt?
=
?conn.prepareStatement(sql);
??????ResultSet?rs?
=
?stmt.executeQuery();
ArrayList?array
=
new
?ArrayList();

while
?(rs.next())?
{
???AActionForm?actionForm?
=
new
?AActionForm?();
???actionForm.setId(rs.getString(
"
Id
"
));
???actionForm.setName(rs.getString(
"
Name
"
));???????????
???array.add(actionForm);
}
在Action 的Execute方法中,我們 把這個集合用request.setAttribute("array", array)存儲起來,然后在JSP頁面中,我們用iterate Tag把數據循環現實出來。代碼通常都是這個樣子:
?
<
logic:present?
name
="?array?"
?scope
="request"
>
<
logic:iterate?
name
="?array?"
?id
="?array?"
?
type
="com.bhsky.webis.Goods"
>
??
<
tr?
align
="center"
>
?
?????
<
td?
class
="table2"
>
<
bean:write?
name
="?array?"
?property
="goodsid"
/>
</
td
>
??
<
td?
class
="table2"
>
<
bean:write?
name
="?array?"
?property
="goodsname"
/>
</
td
>
???
</
tr
>
??
</
logic:iterate
>
?
</
logic:present
>
在Struts中,對數據的訪問和顯示的寫法通常都是很固定的,在VIEW層,我們是沒有辦法簡化自己的代碼的,在Action層,其寫法通常也很固定,只是做一個頁面的跳轉,商業邏輯和對數據得訪問,通常都是放在JavaBean中。那么,在此,我提出一種運用類反射的機制,使應用程序對ActionForm Bean的賦值自動化,即應用程序通過一個簡單的接口,使用一個通用的方法,就可以完成對ActionForm Bean的賦值,而不必在每個使用ActionFormBean的地方,都把數據庫中的值手動賦值給ActionForm Bean,然后再在JSP頁面中顯示出來。雖然它不能減少ActionForm Bean的數量,但是,它至少使應用程序對ActionForm Bean的賦值自動化了,從而減少了程序出錯概率,提高了程軟件開發效率。
類反射的概念:
關于類反射的概念,在此我就不詳細介紹了,它不是本文的重點,IBM developerWorks網站上有大量介紹類反射概念的文章,大家可以找出來參考一下。其實,Struts本身就大量利用了類反射的機制。
如何應用類反射機制簡化Struts應用程序的開發:
1、 先定義Action FormBean:
????
package
?com.bhsky.webis.system;
import
?org.apache.struts.action.
*
;
import
?javax.servlet.http.
*
;
????

public
?
class
?UsersActionForm?
extends
?ActionForm?
{
private
?String?usr_id;
private
?String?usr_name;

public
?
void
?setUsr_id(String?usr_id)?
{
????
this
.usr_id?
=
?usr_id;
}
public
?String?getUsr_id()?
{
????
return
?usr_id;
}
public
?String?getUsr_memo()?
{
????
return
?usr_memo;
}
public
?
void
?setUsr_name(String?usr_name)?
{
????
this
.usr_name?
=
?usr_name;
}
}
2、 編寫通用的為ActionFormBean賦值的方法:
???
////////////////////////////////////////////////////////////////////////////
/
????
//
Function:?完成ResultSet對象向ArrayList對象為集合的對象的轉化
????
//
Para:sql,指定的查詢Sql
???
//
Para:className,Sql相對應得JavaBean/FormBean類的名字
???
//
Return:以類className為一條記錄的結果集,完成ResultSet對象向ArrayList對象為集
//
合的className對象的轉化
??
//////////////////////////////////////////////////////////////////////////////

??
public
?ArrayList?Select(String?sql,String?className)
{
????ArrayList?paraList
=
new
?ArrayList();

????
try
{

??????
if
?(conn?
==
?
null
)
{
????????Connection();
??????}
??????PreparedStatement?stmt?
=
?conn.prepareStatement(sql);
??????ResultSet?rs?
=
?stmt.executeQuery();
??????String?recordValue
=
""
;
??????Object?c1
=
null
;
??????paraList
=
new
?ArrayList();
??????ResultSetMetaData?rsmd?
=
?rs.getMetaData();
??????
int
?columnCount?
=
?rsmd.getColumnCount();

??????
while
?(rs.next())
{
??????????c1
=
Class.forName(className).newInstance();

??????????
for
?(
int
?i
=
1
;?i
<=
columnCount;?i
++
)?
{

????????????
if
(rs.getString(rsmd.getColumnName(i))
!=
null
)
{
??????????????recordValue
=
rs.getString(rsmd.getColumnName(i));

????????????}
else
{
??????????????recordValue
=
""
;
????????????}
Method?
m
=
c1.getClass().getMethod(getSetMethodName(rsmd.getColumnName(i)),

new
?Class[]
{recordValue.getClass()}
);

????????????m.invoke?(c1,?
new
?Object[]
{recordValue}
);
??????????}
??????????paraList.add(c1);
??????}
????}
catch
(SQLException?ex)
{
??????

}
catch
(ClassNotFoundException?e)
{

}
catch
(NoSuchMethodException?e)?
{

}
catch
(InvocationTargetException?e)
{

}
catch
?(IllegalAccessException?e)
{

}
catch
(InstantiationException?e)
{

}
?finaly
{
????????closeConnection();
return
?paraList;
}
??}
3、 在JavaBean封裝的商業邏輯中調用Select 方法,然后在JSP頁面上顯示出來:
//
Function:取得用戶列表
??
//
Para:
??
//
Return:返回用戶列表
??
////////////////////////////////////////////////////////////////////////////
/
??
public
?ArrayList?getUsers()
{
??????ArrayList?ret
=
null
;
??????DatabaseManage?db
=
new
?DatabaseManage();
??????String?sql
=
"
?select?usr_id,usr_name?
"
??????????
+
"
?from?users?
"
?;
??????ret
=
db.Select(sql,
"
?com.bhsky.?webis.system.UsersActionForm
"
);
??????
return
?ret;
??}
4、 在Action的execute方法中調用getUsers()方法:
?
public
?ActionForward?execute(ActionMapping?actionMapping,?ActionForm?actionForm,?
?????HttpServletRequest?request,?HttpServletResponse?httpServletResponse)?

???
{

????
/**?*/
/**
@todo:?complete?the?business?logic?here,?this?is?just?a?skeleton.
*/
????UsersActionForm?uaf
=
(UsersActionForm)actionForm;
????SystemService?ubb
=
new
?SystemService();
????ArrayList?userList
=
ubb.getUsers();
????request.setAttribute(
"
userList
"
,userList);
????ActionForward?actionForward
=
actionMapping.findForward(url);
????
return
?actionForward;
??}
5、 在JSP中顯示:
?
???
<
table?
width
="700"
?class
="1"
?border
="1"
?cellspacing
="1"
?align
="center"
>
??
<
tr
>
????
<
td?
class
="list"
?
>
用戶ID
</
td
>
????
<
td?
class
="list"
?
>
姓  名
</
td
>
??
</
tr
>
??
<
logic:present?
name
="userList"
?scope
="request"
>
???????
<
logic:iterate?
name
="userList"
?id
="userList"
?
type
="com.bhsky.webis.system.UsersActionForm"
>
??
<
tr
>
????
<
td?
class
="cell1"
??height
="22"
><
bean:write?
name
="userList"
?
property
="usr_id"
/></
td
>
????
<
td?
class
="cell1"
??height
="22"
><
bean:write?
name
="userList"
?
property
="usr_name"
/></
td
>
?????
</
tr
>
??
</
logic:iterate
>
??????
</
logic:present
>
</
table
>
?
結語:
我們通過運用類反射機制,在一個Struts應用開發中,完成了一個通用查詢方法的實現。它使得程序員擺脫了在每個應用程序中都要編寫枯燥的set、get等方法來訪問ActionForm Bean,從而簡化了Struts應用程序的開發。
?