本系列教程將詳細介紹Struts 1.x的基本原理和使用方法,讀者可以參閱《Struts 2系列教程》來比較Struts 1.x和Struts 2.x的相同點和不同點.
在這篇文章中將以一個簡單的例子(mystruts)來演示如何使用MyEclipse來開發、運行Struts程序,並給出了解決ActionForm出現亂碼問題的方法.讀者可以從本文中了解開發Struts 1.x程序的基本過程.
一、本文給出的程序要實現什麼功能
mystruts是一個錄入和查詢產品信息的程序.為了方便起見,本例中的產品信息表只包括了產品ID、產品名稱和產品價格三個欄位.mystruts的主要功能如下:
1. 接受用戶輸入的產品ID、產品名稱和產品價格.
2. 驗證這些欄位的合法性.如果某些欄位的輸入不合法(如未輸入產品ID),程序會forward到一個信息顯示頁,並顯示出錯原因.
3. 如果用戶輸入了正確的欄位值,程序會將這些欄位值保存到資料庫中,並顯示「保存成功」信息.
4. 用戶輸入產品名稱,並根據產品名稱進行模糊查詢.如果存在符合要求的產品信息.程序會以表格形式顯示這些產品的信息,同時顯示記錄數.如果未查到任何記錄,會顯示「沒有符合要求的記錄!」信息.
二、編寫程序前的準備工作
1. 建立資料庫
在編寫程序之前,需要建立一個資料庫(struts)和一個表(t_products),建立資料庫和表的SQL腳本如下所示:
# 建立資料庫struts # 建立表t_products CREATE TABLE IF NOT EXISTS struts.t_products ( product_id varchar(4) NOT NULL, product_name varchar(50) NOT NULL, price float NOT NULL, PRIMARY KEY (product_id) ) ENGINE=InnoDB DEFAULT CHARSET=gbk; |
2 建立一個支持struts1.x的samples工程
用MyEclipse建立一個samples工程(Web工程),現在這個samples工程還不支持Struts1.x(沒有引入相應的Struts jar包、struts-config.xml文件以及其他和Struts相關的配置).然而,在MyEclipse中這一切並不需要我們手工去加入.而只需要使用MyEclipse的「New Struts Capabilities」對話框就可以自動完成這些工作.
首先選中samples工程,然後在右鍵菜單中選擇「MyEclipse」 > 「New Struts Capabilities」,啟動「New Struts Capabilities」對話框.對默認的設置需要進行如下的改動:
(1)將Struts specification改為Struts 1.2.
(2)將Base package for new classes改為struts.
(3)將Default application resources改為struts.ApplicationResources.
改完后的「New Struts Capabilities」對話框如圖1所示.
在設置完后,點擊Finish按鈕關閉對話框.在向samples工程添加支持Struts的功能后,主要對samples工程進行了三個操作.
(1)引入了Struts 1.2 的jar包(在samples的工程樹中多了一個Struts 1.2 Libraries節點).
(2)在WEB-INF目錄中添加了一個struts-config.xml文件.文件的默認內容如下面的代碼所示:
<?xml version="1.0" encoding="UTF-8"?> |
(3)在WEB-INF中的web.xml文件中添加了處理Struts動作的ActionServlet的配置,代碼如下:
<servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> |
到目前為止,samples工程已經完全支持Struts了.讀者可以看到,如果不使用MyEclipse,那麼上面所列出的配置文件的內容都必須手工輸入.因此,使用MyEclipse來開發Struts程序可以省去很多配置xml文件的工作.
三、實現程序的首頁(index.jsp)
首先在<samples工程目錄>中建立一個mystruts目錄,然後在<samples工程目錄>" mystruts目錄中建立一個index.jsp文件,這個文件的內容如下.
<%@ page pageEncoding="GBK"%> <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%> <html> <head> <title>主界面</title> </head> <body> <table align="center" cellpadding="10" width="100%"> <tr> <td align="right" width="50%"> <%-- 使用Struts tag--%> <html:link forward="newProduct">錄入產品信息</html:link> </td> <td> <html:link forward="searchProduct">查詢產品信息</html:link> </td> </tr> </table> </body> |
在MyEclipse中啟動Tomcat(如果Tomcat處於啟動狀態,在修改完配置文件后,建議在MyEclipse的Servers頁重新發布samples工程,以使修改生效).在IE中輸入如下的URL:
http://localhost:8080/samples/mystruts/index.jsp
我們發現在輸入上面的URL后,在IE中並未顯示正確的運行結果,而是拋出了如下的異常:
java.net.MalformedURLException: Cannot retrieve ActionForward named newProduct
這個異常表明程序並未找到一個叫newProduct的forward(forward將在後面詳細地講述).因此,可以斷定,在JSP中使用forward時,這個forward必須存在.下面我們來添加index.jsp頁面中所使用的兩個forward:newProduct和searchProduct.這兩個forward分別引向了建立產品信息的頁面(newProduct.jsp)和查詢產品信息的頁面(searchProduct.jsp).我們可以在struts-config.xml文件中<struts-config>節點中添加兩個全局的forward,代碼如下:
<global-forwards> |
上面的代碼中所示的newProduct.jsp和searchProduct.jsp目前並不存在(將在以後實現這兩個JSP頁面),現在重新輸入上述的URL,會得到如圖2所示的效果.
圖2
如果想讓index.jsp成為默認的JSP頁面,可以在web.xml中的<welcome-file-list>節點中加入如下的內容:
<welcome-file>index.jsp</welcome-file> |
這時在IE中只要輸入如下的URL就可以訪問index.jsp頁面了.
http://localhost:8080/samples/mystruts
四、實現添加和查詢產品信息頁面
在本節中主要實現了用於輸入產品信息(newProduct.jsp)和查詢產品信息(searchProduct.jsp)的JSP頁面.
在newProduct.jsp頁面中有一個form,在form中含有三個文本框,用於分別輸入產品ID、產品名稱和產品價格.
在<samples工程目錄>"mystruts目錄中建立一個newProduct.jsp文件,代碼如下:
<%@ page pageEncoding="GBK"%> <html> <head> <title>錄入產品信息</title> </head> <body> <%-- 向saveProduct動作提交產品信息 --%> <html:form action="saveProduct"> <table width="100%"> <tr> <td align="center"> 產品編號: <html:text property="productID" maxlength="4" /> <p> 產品名稱: <html:text property="productName" /> <p> 產品價格: </td> </tr> <tr> <td align="center"> <br> <html:submit value=" 保存 " /> </td> </tr> </table> </html:form> </body> |
在searchProduct.jsp頁面中有一個form,為了方便起見,在form中只提供了一個文本框用於對產品名稱進行模糊查詢.在<samples工程目錄>" mystruts目錄中建立一個searchProduct.jsp文件,代碼如下:
<%@ page pageEncoding="GBK"%> |
現在啟動Tomcat,並使用如下兩個URL來訪問newProduct.jsp和searchProduct.jsp:
http://localhost:8080/samples/mystruts/newProduct.jsp
http://localhost:8080/samples/mystruts/searchProduct.jsp
在IE中輸入上面的兩個URL后,並不能顯示出相應的界面,而會拋出JspException異常,表明未找到saveProduct和searchProduct動作.從這一點可以看出,如果在JSP中使用Struts Action,這些Action必須事先在struts-config.xml文件中定義,否則,JSP程序就無法正常訪問.在這兩個頁面所使用的動作(saveProduct和searchProduct)將會在下面的部分介紹.
五、通過模型類操作資料庫
在這一節我們來編寫用於操作資料庫的模型類.由於本例子是Web程序,因此,建議在連接資料庫時使用資料庫連接池.在<Tomcat安裝目錄>"conf"Catalina"localhost目錄中打開samples.xml文件(如果沒有該文件,則建立一個samples.xml文件),在<Context>節點中加入如下的內容:
配置連接池(用於連接資料庫struts)
<Resource name="jdbc/struts" auth="Container" |
本例中提供了兩個可以操作資料庫的模型類:Product和SearchProduct.其中Product用於驗證由客戶端提交的產品信息,並向t_products表中寫入這些信息.而SearchProduct類用於對t_products表的product_name欄位進行模糊查詢,並返回查詢到的產品信息(包括產品ID、產品名稱和產品價格).
由於Product和SearchProduct都需要使用資料庫連接池來連接資料庫,因此,可以將連接資料庫的工作提出來作為一個父類(Struts類)提供,代碼如下:
package util; } |
在<samples工程目錄>"src目錄中建立一個Product.java文件,代碼所示:
package mystruts.model; { try { String productID = form.getProductID(); String productName = form.getProductName(); float price = form.getPrice(); String sql = "INSERT INTO t_products VALUES('" productID "'," "'" productName "'," String.valueOf(price) ")"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.executeUpdate(); // 執行INSERT語句 conn.close(); } catch (Exception e) { throw new Exception(e.getMessage()); } } } |
在Product類中使用了一個ProductForm類,這個類是一個ActionForm類,它的功能是保存客戶端提交的數據.關於這個類將在下面詳細介紹.Product類通過構造方法的form參數將客戶端提交的數據傳入Product類的對象實例中,並在構造方法中驗證這些數據,如果發現數據不合法,就會拋出一個異常.當客戶端提交的數據合法后,成功建立了一個Product類的對象實例,然後可以通過簡單地調用save方法將數據保存到t_products表中.
與Product類似,在<samples工程目錄>"src目錄中建立一個SearchProduct.java文件,代碼如下:
package mystruts.model; public SearchProduct(ProductForm form) throws Exception { super(); this.form = form; } // 查詢產品信息,並通過List返回查詢結果 public List<String[]> search() throws Exception { List<String[]> result = new LinkedList<String[]>(); String sql = "SELECT * FROM t_products WHERE product_name like '%" form.getProductName() "%'"; PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery(); // 開始執行SELECT語句 { String[] row = new String[3]; row[0] = rs.getString(1); row[1] = rs.getString(2); row[2] = rs.getString(3); result.add(row); } rs.close(); conn.close(); return result; } } |
在SearchProduct類也使用了ProductForm類,但在SearchProduct中並不會驗證ProductForm對象實例中的數據,而只是將ProductForm對象作為傳遞查詢請求信息(實際上只需要產品名稱)的工具而已.
六、實現控制器
在這一節要實現的控制器是基於Struts的Web程序的核心部分之一:控制器實質上也是普通的Java類,但這個Java類一般要從org.apache.struts.action.Action類繼承.控制器的主要功能是接受並處理從JSP頁面提交的數據、通過模型(Model)和資料庫交互以及forward到相應的頁面(可以是任何頁面,如html、JSP和Servlet等).在實現控制器之前,需要先實現一個ActionForm類, 這個類的作用是保存JSP頁面提交的數據.在<samples工程目錄>"src目錄中建立一個ProductForm.java文件,代碼如下:
import org.apache.struts.action.*; public class ProductForm extends ActionForm { private String productID; // 產品ID private String productName; // 產品名稱 private float price; // 產品價格 public String getProductID() { return productID; } public void setProductID(String productID) { this.productID = productID; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } } |
從上面的代碼可以看出,ActionForm類一般從org.apache.struts.action.ActionForm類繼承,在類中需要按著需要保存的數據表欄位添加屬性.如產品ID的屬性是productName.在MyEclipse中可以只定義三個private變數,然後使用MyEclipse的「Source」 > 「Generate Getters and Setters……」功能自動產生getter和setter方法.但在給這些屬性取名時要注意,private變數的名子和數據表的欄位名沒有直接的關係,但必須和JSP頁面中的<html>標籤的property屬性值一致,如<html:text property="productName" />表示輸入產品名稱的文本框,其中property屬性的值就是ProductForm類中的productName變數.如果不一致,將會拋出異常.其他和ProductForm類的屬性對應的<html>標籤可以查看上面的代碼.
光有ActionForm類還不夠,還需要在struts-config.xml中的<struts-config>節點中添加如下的內容:
<form-beans> |
上面的代碼所配置的兩個ActionForm實際上指的是同一個ProductForm類,但這個ProductForm類在後面要講的兩個動作里都要使用,為了更容易理解,為這個ProductForm起了兩個不同的別名(saveProductForm和searchProductForm).
下面來實現saveProduct動作的代碼.Struts Action類必須一般從org.apache.struts.action.Action類繼承.一般在Struts Action類需要覆蓋Action類的execute方法.這個方法有每次客戶端訪問Struts Action時調用.我們可以在方法中處理客戶端提交的數據,訪問資料庫等工作.這個方法返回一個ActionForward類型的值,表明在執行完execute后,要forward到的頁面.描述saveProduct動作的類叫SaveProductAction.代碼如下:
package mystruts.action; HttpServletRequest request, HttpServletResponse response) { ProductForm saveProductForm = (ProductForm) form; try { Product product = new Product(saveProductForm); product.save(); // 保存產品信息 request.setAttribute("info", "保存成功!"); } catch (Exception e) { request.setAttribute("info", e.getMessage()); } return mapping.findForward("save"); } } |
在SaveProductAction類中使用了模型類Product驗證並保存產品信息.並將操作結果信息保存在request的屬性中,key為「info」.在execute的,使用了ActionMapping類的findForward方法在struts-config.xml中尋找一個叫「save」的forward.這個forward是一個JSP頁,用於顯示是否將產品信息保存成功的信息.為了可以在struts-config.xml中查找這個forward,需要在struts-config.xml的<action-mappings>節點中加入如下的內容.
<action name="saveProductForm" path="/saveProduct"scope="request" type=" mystruts.action.SaveProductAction"> |
從上面的代碼可以看出,那個用於顯示保存狀態信息的JSP頁面叫save.jsp.在<samples工程目錄>"mystruts目錄中建立一個save.jsp文件,代碼如下:
<%@ page pageEncoding="GBK"%> |
在IE中輸入如下的URL:
http://localhost:8080/samples/mystruts/newProduct.jsp
在文本框中輸入相應的信息后,點「保存」按鈕,如果輸入的數據是合法的,就會將數據保存在t_products中,否則會顯示出錯的原因. searchProduct動作的實現和saveProduct差不多,也會為三步:實現動作類(SearchProductAction)、在struts-config.xml中添加配置信息和實現用於顯示查詢結果的JSP文件.下面的代碼分別顯示了這三步所要編寫的代碼.
SearchProductAction.java
package mystruts.action; { request.setAttribute("result", result); request.setAttribute("info", "記錄數:" String.valueOf(result.size())); } else // 沒有查到任何產品信息 request.setAttribute("info", "沒有符合要求的記錄!"); } catch (Exception e) { request.setAttribute("info", e.getMessage()); } return mapping.findForward("search"); } } |
在struts-config.xml中配置searchProduct動作
<action name="searchProductForm" path="/searchProduct" scope="request" type="mystruts.action.SearchProductAction"> |
search.jsp
<%@ page pageEncoding="GBK"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <body> <%-- 從request的result中取出查詢結果 --%> <c:set var="result" value="${requestScope.result}" /> <table width="100%"> <tr align="center"> <td> ${requestScope.info} </td> </tr> <tr align="center"> <td> <logic:present name="result"> <table border="1"> <tr align="center"> <td> 產品ID </td> <td> 產品名稱 </td> <td> 價格 </td> </tr> <logic:iterate id="row" name="result"> <tr> <td> ${row[0]} </td> <td> ${row[1]} </td> <td> ${row[2]} </td> </tr> </logic:iterate> </table> </logic:present> </td> </tr> </table> </body> |
在IE中輸入如下的URL:
http://localhost:8080/samples/ mystruts/searchProduct.jsp
在「產品名稱」文本框中輸入產品名稱的一部分,程序就會查詢出所有包含輸入的產品名稱的產品信息,並將結果顯示出來.
七、解決ActionForm的亂碼問題
到現在為止,程序的功能部分已經全部實現完了.但還存在一個問題.當我們在產品名稱中輸入中文時,雖然將客戶端提交的數據成功保存到資料庫中,但是在t_products表中的product_name欄位顯示的都是亂碼.產生這個問題的原因只有一個,就是客戶端提交的數據的編碼格式和資料庫的編碼格式不一致造成的.當然,解決這個問題的方法有很多,但筆者認為最容易的就是使用過濾器.所謂過濾器,就是在客戶端提交數據后,在交由服務端處理之前所執行的一段服務端代碼(一般為Java代碼).一個過濾器是一個實現javax.servlet.Filter介面的類.在本例中要使用的過濾器類叫EncodingFilter,實現代碼如下:
EncodingFilter.java
package filter; chain.doFilter(request, response); } public void init(FilterConfig filterConfig) throws ServletException { } } |
Filter介面的doFilter方法是過濾器的核心方法.其中FilterChain類的doFilter方法允許繼續處理客戶端提交的數據.我們還可以使用這個方法來臨時關閉Web站點的某個或全部的頁面(根據過濾器的設置而定).由於本書的資料庫使用的是GBK編碼格式,因此,需要使用ServletRequest的setCharacterEncoding方法將客戶端提交的數據也設為GBK編碼格式.
除了實現過濾器類,我們還需要在web.xml中的<web-app>節點加入如下的配置信息才能使過濾器生效:
在web.xml中配置過濾器
<filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
在重新啟動Tomcat后,重新輸入一條帶中文的產品信息,看看是否可以將中文保存在資料庫中?
[火星人 ] Struts1.x系列教程(1):用MyEclipse開發第一個Struts程序已經有837次圍觀