[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应用程序的开发。