新增一個Struts配置文件
考慮到圖書模塊是一個比較獨立的模塊,為了避免對Struts配置文件的資源爭用導致團隊工程的覆蓋或沖突,我們為這個模塊單獨提供一個新的Struts配置文件,用這個配置文件配置圖書模塊所有Struts關聯的信息。
我們按照如下的方式為webModule模塊添加一個名為book-struts-config.xml的配置文件。
首先到<工程目錄>/webModule/WEB-INF拷貝一個原有的struts-config.xml文件,更名為book-struts-config.xml放在struts-config.xml相同的目錄下,刪除原有配置的內容,將其調整成:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
</struts-config>
然後,在工程窗格的資源樹中定位到webModule->Deployment descriptors-><Struts 1.1>節點上,右擊<Struts 1.1>節點,在彈出的菜單中選擇Properties...彈出Properties for ’<Struts 1.1>’對話框,如圖 16所示:
圖 16 Struts配置文件維護對話框
點擊Add...按鈕,在彈出的Choose Struts config file對話框中選擇book-struts-config.xml配置文件,按OK這個新的Struts配置文件將添加到Struts config file in web.xml列表中。
新增配置文件成功後,在工程窗格資源樹的<Struts 1.1>節點下,你將會發現這個新加入的Struts配置文件,如下圖所示:
圖 17 兩個Struts配置文件
這樣,在創建新的FormBean或Action時,你就可以選擇用哪個配置文件來保存Struts的配置信息了。
圖書Action Form
下面我們著手創建用於接收新增圖書頁面表單數據的BookActionForm,使用book-struts-config.xml保存BookActionForm的配置信息。BookActionForm需要進行數據有效性自檢,也就是說,要讓BookActionForm實現validate()方法。
創建BookActionForm和創建UserActionForm相似,但是在向導的第1步需要指定book-struts-config.xml記錄BookActionForm配置信息,如圖 18所示:
圖 18 選擇不同的配置文件
我們在前一節為Web模塊添加了一個配置文件,在Struts config下拉框中列出了Web模塊所有配置文件,這裡我們選擇WEB-INF/book-struts-config.xml。
在向導的第2步,我們為BookActionForm定義下列5個屬性:
String bookId;//圖書ID,對應T_BOOK表的BOOK_ID,是主鍵。
String isbn;//isbn
String createDate;//創建日期
String bookName;//書名
String author;//作者
在向導的第2步直接按Finish創建BookActionForm。由於bookId屬性是主鍵,所以不能和T_BOOK中已有的記錄重復,這可以通過BookActionForm的數據自檢機制來完成,數據自檢是通過定義validate()方法來實現的。向導已經為BookActionForm生成了validate()方法框架,我們只需要在validate()方法編寫bookId的校驗的代碼就可以了,BookActionForm的最終代碼如代碼清單 10所示:
代碼清單 10 BookActionForm.java
1. package bookstore;
2.
3. import javax.servlet.http.HttpServletRequest;
4. import org.apache.struts.action.*;
5. import java.sql.*;
6.
7. public class BookActionForm
8. extends ActionForm {
9. …
10. public ActionErrors validate(ActionMapping actionMapping,
11. HttpServletRequest httpServletRequest) {
12. ActionErrors errors = new ActionErrors();
13. Connection conn = null;
14. try {
15. conn = DBConnection.getConnection();
16. PreparedStatement pStat = conn.prepareStatement(
17. "select count(*) count from T_BOOK where BOOK_ID=?");
18. pStat.setString(1, this.bookId);
19. ResultSet rs = pStat.executeQuery();
20. if (rs.next()&& rs.getInt(1) > 0) {
21. errors.add("bookId ",
22. new ActionMessage("bookstore.duplicate.bookId",
23. "圖書ID和數據庫中已經有的ID重復"));
24. }
25. }
26. catch (SQLException se) {
27. se.printStackTrace();
28. errors.add("bookId",
29. new ActionMessage("bookstore.dbaccess.error", "訪問數據庫時出錯"));
30. }
31. finally {
32. try {
33. if (conn != null) {
34. conn.close();
35. }
36. }
37. catch (SQLException ex) {
38. ex.printStackTrace();
39. errors.add("bookId",
40. new ActionMessage("bookstore.dbaccess.error",
41. "訪問數據庫時出錯"));
42. }
43. }
44. return errors;
45. }
46.
47. public void reset(ActionMapping actionMapping,
48. HttpServletRequest servletRequest) {
49. this.createDate = getCurrDateStr();
50. }
51.
52. //獲取當前時間字符
53. private static String getCurrDateStr() {
54. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
55. return sdf.format(new Date());
56. }
57. }
當用戶提交表單後,Struts框架自動把表單數據填充到ActionForm中,接著Struts框架自動調用ActionForm的validate()方法進行數據驗證。如果validate()方法返回的ActionErrors為null或不包含任何ActionMessage對象,表示通過驗證,Struts框架將ActionForm和HTTP請求一起傳給Action的execute(),否則Struts框架將HTTP請求返回到輸入的頁面中,而輸入頁面即可通過<html:errors>顯示對應request域中的ActionErrors錯誤信息顯示出來。
此外,我們在reset()方法中將createDate屬性置為當前的日期,因為這個屬性值不是通過頁面表單提供的。
新增圖書JSP頁面
1.通過向導創建bookAdd.jsp
通過JSP向導創建bookAdd.jsp頁面,在向導的第2步選擇使用Struts1.1的struts-bean和struts-html標簽,如圖 19所示:
圖 19 指定選用Struts標簽
2.使用JBuilder的Struts標簽構建JSP頁面
你可以直接用拖拽的方法從JBuilder編輯器左邊的標簽庫將Struts標簽添加到JSP頁面中,如圖 20所示:
圖 20 用拖拽的方式添加Struts標簽
Struts的html標簽可以完成和標准的HTML元素相同的功能,Struts提倡使用Struts html標簽庫,因為這些標簽可以和Struts框架的其他組件緊密地聯系起來。而Strtus的bean標簽庫可以訪問已經存在的JavaBean及其屬性,有一些bean標簽還可以訪問HTTP請求頭信息及Web資源文件的信息。
我們希望用Struts的html標簽庫創建添加圖書的表單,通過bean標簽庫訪問Web資源文件作為表單組件前的標識文字。
bookAdd.jsp的最終代碼如代碼清單 11所示:
代碼清單 11 bookAdd.jsp
1. <%@page contentType="text/html; charset=GBK" %>
2. <%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
3. <%@taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
4. <html>
5. <head>
6. <title>bookInsert</title>
7. <script language="JavaScript" >
8. function mySubmit(form)
9. {
10. if(form.isbn.value == null || form.isbn.value == "")
11. {
12. alert("圖書的ISBN不允許為空");
13. return false;
14. }
15. if(form.bookName.value == null || form.bookName.value == "")
16. {
17. alert("圖書名不允許為空");
18. return false;
19. }
20. }
21. </script>
22. </head>
23. <body bgcolor="#ffffff">
24. <html:errors/>
25. <html:form action="/bookInsertAction.do" focus="bookId" method="post"
26. onsubmit="return mySubmit(this)" >
27. <table width="100%%" border="0">
28. <tr>
29. <td>
30. <bean:message bundle="bookstore" key="bookstore.bookId"/>
31. </td>
32. <td>
33. <html:text name="bookActionForm" property="bookId"/>
34. </td>
35. <td>
36. <bean:message bundle="bookstore" key="bookstore.isbn"/>
37. </td>
38. <td>
39. <html:text name="bookActionForm" property="isbn"/>
40. </td>
41. </tr>
42. <tr>
43. <td>
44. <bean:message bundle="bookstore" key="bookstore.bookName"/>
45. </td>
46. <td>
47. <html:text name="bookActionForm" property="bookName"/>
48. </td>
49. <td>
50. <bean:message bundle="bookstore" key="bookstore.author"/>
51. </td>
52. <td>
53. <html:text name="bookActionForm" property="author"/>
54. </td>
55. </tr>
56. <tr align="center">
57. <td colspan="4">
58. <html:submit value="保存"/>
59. <html:reset value="取消"/>
60. </td>
61. </tr>
62. </table>
63. </html:form>
64. </body>
65. </html>
其中第25~63行是表單的定義代碼,將<html:form>的action指定為"/bookInsertAction.do", 它是BookInsertAction的訪問URI,將在下一節實現,通過<html:form>訪問Action時,action只需保證和配置文件中指定的path一致就可以了,無需在前面添加上諸如/webModule的Web部署子目錄。
在第26行我們為<html:form>指定了一個onsubmit客戶端校驗函數,當isbn和bookName兩組件中的任何一個為空時,拒絕提供表單。
我們定義了4個<html:text>,它們對應標簽HTML的<input type="text">輸入框標簽,其中name屬性為對應的ActionForm名字,而property對應ActionForm的屬性。圖 21是bookAdd.jsp的設計期效果圖:
圖 21 bookAdd.jsp設計時的界面圖
當然,你可以直接在表單組件前寫入具體的標識,如"圖書ID",而非第30行的<bean:message>標簽,但後者通過一個資源文件產生具體的標識,這樣不但可直接通過資源文件控制標識還提供了國際化的特性。
上面,我們只是簡單地引用了名為bookstore的資源文件,下面我們需要創建這個資源文件並在Struts配置文件中描述它。
到<工程目錄>/src目錄下,創建一個名為bookStoreResource_zh_CN.properties的資源文件,其內容如下所示:
bookstore.bookId=\u56fe\u4e66IDbookstore.isbn=ISBNbookstore.bookName=_
\u56fe\u4e66\u540dbookstore.author=\u4f5c\u8005bookstore.dbaccess.error=_
\u6570\u636e\u5e93\u8bbf\u95ee\u9519\u8befbookstore.duplicate.bookId=_
\u56fe\u4e66\u7f16\u53f7\u548c\u6570\u636e\u5e93\u4e2d\u5df2\u6709\u7684\u7f16\u53f7\u91cd\u590d
這兒的中文必須采用Unicode編碼格式,其對應的中文文件的內容為:
bookstore.bookId=圖書IDbookstore.isbn=ISBNbookstore.bookName=圖書名
bookstore.author=作者bookstore.dbaccess.error=數據庫訪問錯誤
bookstore.duplicate.bookId=圖書編號和數據庫中已有的編號重復
在編譯工程時,JBuilder會將位於<工程目錄>/src下的資源文件拷貝到Web模塊的WEB-INF/classes目錄下。
提示:
JDK提供了一個將中文轉換為Unicode編碼格式的工具native2ascii.exe,它位於<JDK>/bin/目錄下。在DOS命令窗口下,通過native2ascii -encoding GBK <源文件>
<目標文件>即可以完成轉換。
注意:
在前文中,我們曾提到了JBuilder 2005的一個Bug,即Web模塊中的類或資源有時得不到同步,需要手工將類和資源拷貝到對應的目錄。如果你發現資源文件沒有同步到WEB-INF/classes目錄時,bookStoreResource_zh_CN.properties需要在編譯工程後手工拷貝到這個目錄下,否則Struts就無法找到資源了。
在工程窗格的資源樹中找到book-struts-config.xml雙擊打開其對應的Struts Config Editor,切換到Message Resources標簽頁,如所示:
圖 22 定義資源文件
通過Add...按鈕定義一個鍵名為bookstore的bookStoreResource資源文件。不同的語言環境的客戶端將會訪問不同的資源文件,如客戶端為中文環境時,將訪問bookStoreResource_ch_CN.properties,如果是英語的客戶端將訪問bookStoreResource_cn.properties資源文件,如果沒有找到對應語言的資源文件,將訪問默認的資源文件,這裡,默認資源文件為bookStoreResource.properties。<bean:message>的boudle即為Parameter欄定義的名稱。
實戰經驗:
筆者原來對Struts標簽是比較排斥的,因為雖說它本意希望將頁面展現邏輯和程序邏輯完美地分離開來。頁面設計的工作由頁面設計人員在Dreamweaver中完成,由於Dreamweaver不認識Struts的標簽,那些像<html:text>,<html:submit>等表單組件的Struts標簽在頁面設計時看不到效果,所見即所得的理念被無情的剝奪了,界面設計人員對包含Struts標簽的頁面常常感到如墮五裡霧中。
許多Struts開發人員一直夢寐以求,希望能得到一個Dreamweaver的Struts標簽擴展插件,讓頁面設計人員可以象標准的HTML組件標簽一樣設計頁面。FWA公司的Visual Tags for Struts插件終於使我們夢想成真。通過這一插件,可以將Dreamweaver完美地和Struts結合起來,使用Struts標簽的JSP頁面可以在設計期得到使用標准HTML標簽一樣的可視化效果。你可以通過http://www.fwasi.com/downloads/下載Visual Tags for Struts的試用版。圖 21的bookAdd.jsp效果界面,即是在Dreamweaver 2004中使用了Visual Tags for Struts插件後的效果頁面。
此外,有一點關於Struts標簽的事情也必須提及:Sun開發出了JSF頁面標簽,這和Struts的標簽在功能上是有重疊的,Struts 以後的版本將逐漸往JSF靠近,JSF的標簽可能將最終取代Struts自己的標簽,以實現天下大統。
創建BookInsertAction
下面,我們來創建BookInsertAction,在該Action中將圖書記錄添加到T_BOOK表中。如果操作成功定向到insertSuccess.htm操作成功頁面,如果在進行數據庫操作時發現SQLException,則轉向sqlFail.htm頁面。我們需要事先創建這兩個HTML頁面,為了簡單,僅在其中寫入報告操作成功和失敗的信息即可。
按3.2相似的方式創建BookInsertAction,用book-struts-config.xml記錄配置信息,在向導的第2步,將FormBean name指定為bookActionForm,Scope為request,將input JSP指定為/bookAdd.jsp,如圖 23所示:
圖 23 指出BookInsertAction的配置信息
按Finish直接創建BookInsertAction,JBuilder將打開Struts Config Editor顯示/bookInsertAction的流程,如圖 24所示:
圖 24 bookInsertAction流程
添加1個出口,名為success,路徑為/insertSuccess.htm。最終的/bookInsertAction的流程如圖 5所示。
代碼清單 12是BookInsertAction的代碼,它完成圖書添加,出口控制的操作:
代碼清單 12 BookInsertAction.java
1. package bookstore;
2.
3. import javax.servlet.http.*;
4. import org.apache.struts.action.*;
5. import java.sql.*;
6.
7. public class BookInsertAction
8. extends Action {
9. public ActionForward execute(ActionMapping actionMapping,
10. ActionForm actionForm,
11. HttpServletRequest servletRequest,
12. HttpServletResponse servletResponse)
13. throws Exception
14. {
15. BookActionForm bookActionForm = (BookActionForm) actionForm;
16. Connection conn = null;
17. conn = DBConnection.getConnection();
18. PreparedStatement pStat = conn.prepareStatement(
19. " insert into T_BOOK1(BOOK_ID,ISBN,BOOK_NAME,AUTHOR,"+
20. "CREATE_DATE) values(?,?,?,?,?)");
21. pStat.setString(1, bookActionForm.getBookId());
22. pStat.setString(2, bookActionForm.getIsbn());
23. pStat.setString(3, bookActionForm.getBookName());
24. pStat.setString(4, bookActionForm.getAuthor());
25. pStat.setString(5, bookActionForm.getCreateDate());
26. pStat.executeUpdate();
27. return actionMapping.findForward("success");
28.
29. }
30. }
BookInsertAction將bookActionForm的數據通過JDBC添加到T_BOOK表中,添加成功則轉向insertSuccess.htm頁面。有些觀察細致的讀者也許已經注意到BookInsertAction的execute()方法並未直接對SQLException進行處理,而是將異常拋出,如第13行所示。這裡,我們要用到Struts1.1的新功能:通過配置方式處理異常。
在工程窗格的webModule/Deployment descriptors/<Struts 1.1>下找到並雙擊book-struts-config.xml文件,調出的Struts Config Editor配置編輯器,切換到Global Exceptions標簽頁,如圖 25所示:
圖 25 異常處理配置
點擊Add...定義一個名為sqlexception的異常處理配置項,處理java.sql.SQLException異常,定義完這個配置項後,選中這個配置項,點擊Edit...切換到這個配置項的詳細設置頁面,如圖 26所示:
圖 26 異常處理配置窗口
在窗口下部切換到Source視圖頁中,這個異常配置項的配置信息如代碼清單 13所示:
代碼清單 13 SQLException的異常處理配置項
1. …
2. <struts-config>
3. …
4. <global-exceptions>
5. <exception key="sqlexception" type="java.sql.SQLException"
6. path="/sqlFail.htm"/>
7. </global-exceptions>
8. …
9. </struts-config>
第5~6行將/sqlFail.htm和java.sql.SQLException"掛接"起來,這樣程序中任何Action的execute()方法拋出的SQLException異常都將由sqlexception配置項處理。
在welcome.jsp頁面中添加一個調用bookAdd.jsp的鏈接,登錄系統後,通過這外鏈接調出bookAdd.jsp頁面,如圖 27所示:
圖 27 添加圖書頁面
填寫圖書記錄,點擊保存提交表單到/bookInsertAction的Action中,如果提供的圖書ID和T_BOOK中已有的BOOK_ID重復,則Struts總控制器將直接返回到bookAdd.jsp頁面中,由<html:errors/>報告錯誤信息。如果數據通過BookActionForm的自檢並成功添加到數據庫中,將最終轉向insertSuccess.htm頁面。
提示:
如果用Tomcat為Web服務器,如果圖書記錄會出現中文亂碼的問題,可以使用我們上面介紹過的一個編碼過濾器,你可以在web.xml中配置這個過濾器,亂碼問題就可以解決了。