新增一个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中配置这个过滤器,乱码问题就可以解决了。 |
|