電子商務(wù)模型的JSP、JavaBean實(shí)現(xiàn) |
引言 現(xiàn)在,開發(fā)和管理一個(gè)電子商務(wù)系統(tǒng)需要高效率的開發(fā)和利用網(wǎng)絡(luò)資源,特別是如果你想讓你的顧客在網(wǎng)上購買你的產(chǎn)品或是取得你提供的服務(wù)的話,更要注意這一點(diǎn)。構(gòu)建一個(gè)這樣的商務(wù)網(wǎng)站來實(shí)現(xiàn)你商業(yè)上的目的并不是一件非常簡單的工作,在開發(fā)電子商務(wù)網(wǎng)站的時(shí)候,我們就要充分的利用搞技術(shù)含量的技術(shù),我們可以利用到最先進(jìn)的Java 技術(shù):Java Server Pages(JSP),Java Servlets 和JavaBeans(甚至是EJB),它們各自都有自己的不同的優(yōu)點(diǎn),因此了解在構(gòu)建一個(gè)電子商務(wù)網(wǎng)站時(shí)如何合理的利用它們各自的優(yōu)勢,并且把它們聯(lián)合起來以完成你想達(dá)到的效果是非常重要的。 當(dāng)然,我們可以只使用 JSP來構(gòu)建電子商務(wù)系統(tǒng),比如一個(gè)簡單的購物車系統(tǒng),但是如果你要想完成一個(gè)有效的的應(yīng)用程序,并使它用于商業(yè)上,則需要綜合以上我所說的三種技術(shù)相互補(bǔ)充的力量。讓我們來看怎么把它們聯(lián)合起來以完成最好的效果吧!我們都知道,JSP是Sun公司倡導(dǎo)的用來替代微軟ASP的純Java替代品,JSP技術(shù)發(fā)展壯大了Java Servlet技術(shù),事實(shí)上, JSP引擎在運(yùn)行JSP時(shí)也把JSP頁面翻譯成 Servlets;而不用我多說,大家一定都知道Servlets在網(wǎng)絡(luò)編程世界是非常流行的,因?yàn)樗诶碚撋虾蛯?shí)踐上都是可以完全取代 CGI腳本的,Servlets能產(chǎn)生動態(tài)的網(wǎng)頁,這是通過把靜態(tài)的HTML與數(shù)據(jù)庫查詢或事務(wù)性服務(wù)提供的內(nèi)容混合起來實(shí)現(xiàn)的。JSP則是通過在HTML頁面中內(nèi)嵌Java代碼這條途徑來實(shí)現(xiàn)生成動態(tài)的網(wǎng)頁的目的的,把Java代碼插入到HTML頁的這種能力極大的增加了基于Java Servlet網(wǎng)絡(luò)體系結(jié)構(gòu)的靈活性。 為了產(chǎn)生 HTML , servlet 必須用println()輸出格式化的HTML字符串,如: out.println("<html>"); out.println("<br><br>購物車系統(tǒng)"); out.println("</html>"); 從上面的代碼段中可以看出,servlet用println()輸出HTML頁面,也就是說,當(dāng)編寫一個(gè) Java Servlet時(shí),開發(fā)者必須充當(dāng)程序員和網(wǎng)頁設(shè)計(jì)師兩個(gè)身份。而JSP則是在HTML中嵌入簡單的Java代碼,使普通的HTML網(wǎng)頁設(shè)計(jì)師也能寫出優(yōu)秀的動態(tài)網(wǎng)頁,這樣就使網(wǎng)站的設(shè)計(jì)沿著兩條平行的軌道,即Java程序設(shè)計(jì)和HTML頁面設(shè)計(jì)共同進(jìn)行下去,從而加快網(wǎng)站開發(fā)的速度和開發(fā)的質(zhì)量。JSP也支持業(yè)務(wù)邏輯組件和現(xiàn)有的組件之間的寬松連接,從而做到可重用性。 下面,我想通過一個(gè)簡單的購物車程序來說明一下 JSP,Servlet和Bean在網(wǎng)絡(luò)體系結(jié)構(gòu)中是怎樣相互作用的,并且借這個(gè)例子解釋編寫一個(gè)實(shí)際可用的電子商務(wù)應(yīng)用程序應(yīng)該注意的一些問題。 ![]() 簡單購物車的實(shí)現(xiàn)方案 我們的購物車方案實(shí)際上是一種簡化了的在線商店的模型:顧客選擇商品,添加到他們的購物車中,然后通過一系列形式最終購買這些商品。上圖中就顯示了我們的應(yīng)用程序體系結(jié)構(gòu)是如何把 JSP、servlets 和 JavaBeans有機(jī)的結(jié)合起來的,從圖上更可以看出,只使用 JSP來構(gòu)建一個(gè)簡單網(wǎng)絡(luò)應(yīng)用程序是可行,但是一個(gè)有效的應(yīng)用程序是這三種技術(shù)共同作用的結(jié)果。 圖2解釋了model-view-controller( MVC )模式,它把應(yīng)用程序劃分成獨(dú)立的數(shù)據(jù)管理(Model),表現(xiàn)形式(View)和控制組件(Controller),成為最先進(jìn)的圖形用戶接口的基礎(chǔ),這些劃分模塊支持獨(dú)立開發(fā)并且可以重復(fù)使用組件。我們也能把 MVC 模式應(yīng)用于我們的網(wǎng)絡(luò)應(yīng)用程序中:JSP最適合充當(dāng)實(shí)現(xiàn)網(wǎng)絡(luò)應(yīng)用程序的對外表現(xiàn)的部分;而JavaBeans封裝了提供給Web網(wǎng)站的服務(wù)信息內(nèi)容并且簡化了數(shù)據(jù)在體系結(jié)構(gòu)組件之間的傳輸;Servlet則正好充當(dāng)控制者和協(xié)調(diào)用戶請求和應(yīng)用程序信息、更新程序數(shù)據(jù)等功能。 ![]() 好,前面我們已經(jīng)有了“CustomerServlet”大致的設(shè)計(jì)方案,現(xiàn)在讓我們來看看編寫我們應(yīng)用程序上的一些細(xì)節(jié)問題。 代碼1 CustomerServlet.java package shoppingcart; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class CustomerServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } // 處理顧客請求 public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { // 取得請求的Session對象 HttpSession session = request.getSession(true); BasketBean basket = null; file://如果沒有購物車則創(chuàng)建一個(gè)新的如果已存在,則更新它 basket = (BasketBean)session.getAttribute(BasketBean.BASKET); if(basket == null) { // 新的顧客,創(chuàng)建一個(gè)購物車。 basket = new BasketBean(); session.setAttribute(BasketBean.BASKET, basket); } else { // 已存在的顧客,保存籃中的內(nèi)容。 basket.savePurchases(request); } // 取得當(dāng)前的工作流程。 RequestDispatcher rd = null; String nextPage = request.getParameter(BasketBean.PAGE); if (nextPage == null || nextPage.equals(BasketBean.UPDATE)) { // 從目錄中查找選擇 rd = getServletConfig().getServletContext().getRequestDispatcher("Inventory.jsp"); } else if (nextPage.equals(BasketBean.PURCHASE)) { // 提供購買信息 rd = getServletConfig().getServletContext() .getRequestDispatcher("Purchase.jsp"); } else if (nextPage.equals(BasketBean.RECEIPT)) { file:// 提供購買信息 rd = getServletConfig().getServletContext().getRequestDispatcher("Receipt.jsp"); } if (rd != null) { rd.forward(request, response); } } } 上面的程序段顯示了CustomerServlet類中的doGet()和doPost()方法。CustomerServlet類做了兩件事情來控制我們應(yīng)用程序的工作流程: 一、通過 BasketBean 類實(shí)現(xiàn),保持購物車組件的狀態(tài),; 二、它通過一系列的JSP頁面向顧客發(fā)送請求。 一旦我們的購物車與某一個(gè)特定的顧客session相聯(lián)系,顧客的BasketBean對象的實(shí)例就會存儲在 HttpSession對象中。一個(gè)以普通ID進(jìn)入CustomerServlet工作流程的客戶,他會產(chǎn)生很多動作,servlet引擎提供了HttpSession對象來組織并存儲這一系列的相互作用(Session對象為存儲和取回任何使用唯一的鍵/值對的Java對象提供了方便的方法)。在CustomerServlet類中 ,我們首先通過HttpSession session = request.getSession(true)從 servlet 引擎中取得Session對象,大家都可以看到,我們傳遞了true值,意思是我們告訴Servlet引擎,如果一個(gè)session對象已經(jīng)不存在了,就新建一個(gè);然后我們查看Session中是否有我們的購物車,如果我們找不到購物車,我們就知道這個(gè)購物Session剛剛開始,我們必須新建一輛購物車,并且把它保存在Session對象中,如果我們能夠在Session中找到購物車,那我們就知道顧客正處在一個(gè)購物期間,那時(shí)就必須存儲購物車當(dāng)前的狀態(tài)。在查看了購物車的狀態(tài)之后,我們把顧客的請求發(fā)送到相應(yīng)的JSP頁中去,請求本身包含一個(gè)狀態(tài)參數(shù)(BasketBean.PAGE)告訴CustomerServlet把請求發(fā)送到哪里,我們的控制器取回這個(gè)參數(shù),然后使用一個(gè)RequestDispatcher對象把請求提交給下一個(gè)JSP頁。 代碼段2:BasketBean類 package shoppingcart; import javax.servlet.http.HttpServletRequest; import java.util.Hashtable; import java.util.Enumeration; public class BasketBean { final static public String BASKET = "Basket" ; final static public String PAGE = "Page" ; /* 工作流程的狀態(tài) */ final static public String UPDATE = "Update" ; final static public String PURCHASE = "Purchase" ; final static public String RECEIPT = "Receipt" ; /* 當(dāng)前購物籃中有那些商品。 主鍵是商品號 ,值為 Product對象 */ private Hashtable products_ = new Hashtable(); public BasketBean() { } /* 計(jì)算籃中的商品的總價(jià)值。 */ public double getTotal() { double totalPrice = 0.0 ; Enumeration e = products_.elements(); while(e.hasMoreElements()) { Product product = (Product)e.nextElement(); totalPrice += product.getPieces() * product.getPrice(); } return totalPrice; } /* 取得籃中某個(gè)商品的個(gè)數(shù)。 */ public double getPieces(Product p_in_inv) { int SKU = p_in_inv.getSKU(); Product p = (Product)products_.get( Integer.toString(SKU)); if(p == null) return 0.0 ; else return p.getPieces(); } /* 用當(dāng)前的選擇更換籃中的內(nèi)容。 */ public void savePurchases(HttpServletRequest request) { Product[] products = InventoryBean.getCatalogue(); String[] lbValues = request.getParameterValues("pieces" ); if (lbValues != null) { products_.clear(); for (int i = 0 ; i < lbValues.length; i++) { double lbs = Double.parseDouble(lbValues[i]); if(lbs > 0 ) { Product p = null; p = (Product)products[i].clone(); p.setPieces(lbs); products_.put(Integer.toString(p.getSKU()), p); } } } } file://利用一個(gè)函數(shù)實(shí)現(xiàn)統(tǒng)一的顯示商品價(jià)格的方式 public static String getStringifiedValue(double value) { String subval = "0.00" ; if (value > 0.0 ) { subval = Double.toString(value); int decimal_len = subval.length() - (subval.lastIndexOf('.') + 1 ); if(decimal_len > 1 ) subval = subval.substring(0 , subval.lastIndexOf('.') + 3 ); else subval += "0" ; } return subval; } /* 清空籃內(nèi)的東西 */ public void clear() { products_.clear(); } } 代碼段2中 BasketBean類實(shí)現(xiàn)購物車應(yīng)用程序中一個(gè)簡單的數(shù)據(jù)管理模型,它提供了一個(gè)方法,用于取得一個(gè)顧客正在購買的貨物的信息,還提供了一個(gè)方法用來更新購物車的內(nèi)容,我們使用一張哈希表來維護(hù)顧客請求的商品列表。InventoryBean對象管理商品的目錄,我們是使用一個(gè)數(shù)組來實(shí)現(xiàn)商品的目錄的。每個(gè)Product類的實(shí)例存儲四個(gè)屬性:商品名字,商品標(biāo)識號,單價(jià)和購買的數(shù)量,只要顧客購買了東西,Product類的實(shí)例就會改變。 JSP顯示頁面 我們的購物車方案中設(shè)定了3個(gè)JSP 頁面: Inventory.jsp , Purchase.jsp和Receipt.jsp (代碼見下)。應(yīng)用程序發(fā)送Inventory.jsp頁面給新來的顧客,顧客通過不斷的更新Inventory.jsp,來挑選商品;顧客選擇好想要購買的商品以后,應(yīng)用程序就會把顧客的購買請求發(fā)送到Purchase.jsp頁,進(jìn)行處理;最后,顧客證實(shí)確實(shí)購買這些商品,應(yīng)用程序把顧客請求發(fā)送到Receipt.jsp(其過程請參看圖三)。 ![]() 我和說明的方便,我想把JSP的基本內(nèi)容再簡要的向大家介紹一下。JSP頁面是使用特定的JSP標(biāo)記與標(biāo)準(zhǔn)的 HTML混合,除了固定的模板數(shù)據(jù)以外,JSP頁還能包括指令,腳本單元和動作,我們購物車系統(tǒng)也說明了以上三點(diǎn),現(xiàn)在我想就這三個(gè)問題簡單的談一談。 在JSP頁面中,我們可以使用JSP指令將一些與頁面有關(guān)的信息傳遞到JSP引擎,指令的主要作用就是用來與JSP引擎之間進(jìn)行溝通的,JSP中的指令是有語法規(guī)范的:<%@ directive %> page指令 page指令定義了一系列與JSP頁面相關(guān)的屬性,并用它們與JSP引擎進(jìn)行通信。例如, 在Inventory.jsp中使用的一條page指令:<%@ page buffer="5kb" language="java" import="shoppingcart.*" errorPage="Error.jsp" %> 這條指令告訴JSP引擎,輸出緩沖區(qū)大小是5k,在溢出之前輸出緩沖區(qū)的輸出流,它也向JSP引擎說明,當(dāng)前頁使用的腳本語言是Java語言,并請求引擎導(dǎo)入shoppingcart包中的所有Java類,最后,它還指示如果有任何無法處理的錯(cuò)誤,就重定向到Error.jsp頁面中去。因?yàn)樵贘SP1.1中,只能使用 Java作為腳本語言,所以我們可以省略page指令中關(guān)于腳本語言的那部分說明。 include指令用來指定JSP文件被編譯的時(shí)候,需要插入的文本或者代碼,被包含的文件要能夠被JSP引擎控制和訪問。Inventory.jsp文件也使用了include指令: <%@ include file="header.html" %> ?。?@ include file="footer.html" %> 第一個(gè)指令為我們的頁面插入了一個(gè)標(biāo)準(zhǔn)的頁眉;第二個(gè)指令則插入一個(gè)標(biāo)準(zhǔn)的注腳。我們可以使用這些指令為我們的JSP頁面創(chuàng)造一致的外觀。 JSP腳本元素為你提供了把Java代碼插入由當(dāng)前的JSP頁面產(chǎn)生的Servlet功能。在JSP中,有三種腳本語言元素---聲明、小腳本和表達(dá)式。這些元素的語法形式是: ?。?! declaration; %> Inventory.jsp中三種元素都使用了。 下面的JSP片段用來聲明局部變量保存當(dāng)前購物籃(BasketBean的實(shí)例)和產(chǎn)品目錄。 ?。?! BasketBean basket; Product[] catalogue; %> 從上面我們可以看出,JSP 聲明必須以一個(gè)分號結(jié)束,并且 這個(gè)聲明的范圍是整個(gè)JSP頁。 聲明這些局部變量以后,Inventory.jsp 使用一段小腳本從session對象中取回購物車對象(BasketBean)和商名目錄,如下 。 <% basket =(BasketBean) session.getAttribute( BasketBean.BASKET); 所以我們可以看出,JSP 聲明和 JSP小腳本只是放在特定的JSP標(biāo)記之間的Java代碼,當(dāng)JSP引擎把一個(gè)JSP程序翻譯成一個(gè)servlet時(shí),它就把這些Java代碼內(nèi)嵌到新的Servlet代碼中去。 前面我們說過,我們從一個(gè)session對象中取得購物車對象,這個(gè)session對象是一個(gè)內(nèi)部物體。JSP引擎提供一些內(nèi)部隱含對象,這些對象可以直接被引用,我們可以不事先聲明,也不需要專門的代碼來創(chuàng)建他們的實(shí)例。它們在小腳本和表達(dá)式中總是可以使用的,而不需要預(yù)先聲明。JSP 1.1 規(guī)范中列出內(nèi)部隱含對象完整的集合。在程序中,另一個(gè)象這樣的對象就是 HttpServletRequest對象(request),它在CustomerServlet類中作為一個(gè)參數(shù)被傳遞到 doPost()方法中,這就意味著Inventory.jsp 能夠通過調(diào)用request.getSession(true).getAttribute(BasketBean.BASKET)來取回購物車的信息了。 在JSP中表達(dá)式和小腳本為JSP動態(tài)生成網(wǎng)頁提供一個(gè)強(qiáng)有力的工具。從下面的Inventory.jsp程序中我們可以看出,JSP代碼段循環(huán)訪問商品目錄并且動態(tài)地為每個(gè)產(chǎn)品產(chǎn)生HTML表格,我們使用小腳本編寫循環(huán),然后在每一行中都混合使用HTML和JSP表達(dá)式。 (注:JSP引擎把小腳本標(biāo)記之間的Java 代碼直接插入引擎內(nèi)部產(chǎn)生的 servlet 代碼。JSP引擎對待JSP表達(dá)式也是不同的。它先把JSP表達(dá)式變換成字符串,然后在內(nèi)部產(chǎn)生的servlet中把它們包入一個(gè)out.println()調(diào)用中。) 代碼段3 Inventory.jsp ?。?-- Inventory.jsp - 顯示商品目錄并且獲取用戶購買的物品及其數(shù)量單價(jià)等信息 --> <html> ?。?-- 頁面的頭部,使用另一個(gè)HTML頁面,通過include指令來實(shí)現(xiàn) --%> ?。?@ include file="header.html" %> ?。?-- 顯示標(biāo)題 --> ?。糂R> ?。糃ENTER> ?。糡ITLE> 中國科學(xué)技術(shù)大學(xué)百貨商店</TITLE> ?。糉ONT SIZE="+1"> ?。糂> 歡迎選購我們的商品</B> ?。?FONT> ?。糂ODY BGCOLOR="#FFFFF"> ?。?-- Page指令 --> ?。?@ page import="shoppingcart.*" errorPage="error.jsp" %> ?。?-- 創(chuàng)建表單設(shè)定頁面布局 --%> ?。糂R><BR> ?。糉ORM METHOD="post" ACTION="/shoppingcart/CustomerServlet"> ?。糡ABLE WIDTH=450 CELLSPACING="0" CELLPADDING="0" BORDER="1"> ?。?-- 創(chuàng)建表頭 --%> ?。糡R> ?。糡D WIDTH=5% BGCOLOR="#ECA613"> <B>商品序列號</B></TD> ?。糡D BGCOLOR="#ECA613"> <B>商品描述</B></TD> ?。糡D WIDTH=5% ALIGN=center BGCOLOR="#ECA613" > <B>商品數(shù)量</B></TD> ?。糡D WIDTH=25% BGCOLOR="#ECA613"> <B>單價(jià)</B> </TD> </TR> ?。?-- 聲明一個(gè)購物籃,和一個(gè)Product商名目錄。 --%> ?。?! BasketBean basket; Product[] catalogue; %> <%-- 從HttpSession對象中取回當(dāng)前的購物籃,然后從Inventory對象中取回商品列表 --%> <% basket = (BasketBean)session.getAttribute(BasketBean.BASKET); catalogue = InventoryBean.getCatalogue(); %> ?。?-- 循環(huán)顯示出Product中商品列表中的每一個(gè)商品 --%> <% for(int i = 0; i < catalogue.length; i++) { Product product = catalogue[i]; %> ?。糡R> <TD> <%= product.getSKU() %> </TD> ?。糡D> <%= product.getName() %> </TD> ?。糡D> <INPUT TYPE=text SIZE=6 MAXLENGTH=6 NAME="pieces" VALUE=<%= basket.getPieces(product)%>> ?。?TD> ?。糡D ALIGN=right> <%= product.getPrice() %> </TD> </TR> ?。? } %> <%-- 表中顯示總共化去多少錢 --%> ?。糡R> ?。糡D COLSPAN=4 align=right BGCOLOR="#ECA613"><B> 總共人民幣<%= basket.getStringifiedValue(basket.getTotal()) %>元 </B></TD> ?。?TR> ?。?TABLE> ?。?-- 發(fā)送購買請求 --%> ?。糂R> ?。糡ABLE WIDTH=450 CELLSPACING="0" CELLPADDING="0" BORDER="0"> <TR> ?。糡D ALIGN=left><INPUT TYPE=submit NAME=<%= BasketBean.PAGE %> VALUE=<%= BasketBean.UPDATE %>></TD> ?。糡D ALIGN=left><INPUT TYPE=reset VALUE="取消"></TD> <TD ALIGN=right><INPUT TYPE=submit NAME=<%= BasketBean.PAGE %> VALUE=<%= BasketBean.PURCHASE %>></TD> ?。?TR> ?。?TABLE> </FORM> ?。?CENTER> <BR><BR> ?。?@ include file="footer.html" %> </body> ?。?html> Receipt.jsp程序中,我們使用了JSP動作,來處理顧客發(fā)送來請求的參數(shù)值。因此我也想簡要的介紹一下JSP中的動作元素。 除了指令和腳本元素外,JSP動作也是 JSP頁面不可缺少的一部分, 一個(gè)動作元素有兩種基本的語法形式:?。紁refix:tag attr_list /> ?。紁refix:tag attr_list> ?。糱ody> ?。?prefix:tag> 當(dāng)動作有語句體時(shí),我們必須使用后一種表式方法。動作背后基本的概念就是與特定的JSP標(biāo)簽聯(lián)系的“標(biāo)簽處理器”。這些處理器是基于標(biāo)簽的一些代碼段,用來執(zhí)行某些操作。JSP引擎提供了一些標(biāo)準(zhǔn)的動作,所有的這些動作都要以“ jsp ”為前綴。例如,我們的電子商店使用一個(gè)助手Bean來簡化請求參數(shù)分析,我們就要使用<jsp:useBean>元素聲明這樣一個(gè)Bean:<jsp:useBean id="receiptBean" scope="request" class="shoppingcart.ReceiptBean" /> JSP聲明了一個(gè)對象變量,取名為receiptBean,作為 ReceiptBean 的一個(gè)實(shí)例,在當(dāng)前請求完成時(shí)就會被釋放。使用Bean的主要優(yōu)點(diǎn)就是它分析并且返回 HTML請求參數(shù)的簡潔性。 在聲明完Bean以后,我們就可以使用<jsp:setProperty>元素從HTML請求中獲取參數(shù),來設(shè)置它的屬性值。我們可以顯式的指出屬性和HTML 參數(shù)的名字,來設(shè)置屬性值。例如,下面是Receipt.jsp中用來設(shè)置我們的ReceiptBean實(shí)例的屬性的一些語句:<jsp:setProperty name="receipt_bean" property="name" param="name" /> 如果我們的屬性名和相應(yīng)的參數(shù)名相同,我們可以指示用一個(gè)JSP元素來設(shè)置所有的屬性:<jsp:setProperty name="receipt_bean" property="*" /> 這個(gè)單獨(dú)的元素告訴引擎,使用 Java映像來匹配所有的JSP參數(shù)和JavaBean屬性名,然后使用HTML請求信息中的值來設(shè)置JavaBean的屬性值。同樣,我們使用<jsp:getProperty>元素從助手Bean中返回屬性值。例如,下面是我們返回屬性的語句:<jsp:getProperty name="receipt_bean" property="name" /> 在ReceiptBean類的代碼中,每個(gè)在Receipt.jsp中被分析的參數(shù),在我們程序Bean中都有一個(gè)相關(guān)聯(lián)的用來設(shè)置了取得的方法:例如,<jsp:setProperty name="receipt_bean" property="name" param="name" />有一個(gè)相關(guān)聯(lián)的設(shè)置方法:void setName(String phone);同樣, <jsp:getProperty name="receipt_bean" property="name" />也有一個(gè)相關(guān)聯(lián)的取得方法:String getName(); 代碼段4:Receipt.jsp ?。?@ page import="shoppingcart.*" errorPage="error.jsp" %> ?。糷tml> ?。糡ITLE> 中國科學(xué)技術(shù)大學(xué)百貨商店收銀臺</title> ?。糂ODY BGCOLOR="#FFFFF"> ?。糂R><BR> ?。?@ include file="header.html" %> <CENTER> ?。糂R> ?。糉ONT SIZE="+1"> <B> 顧客信息</B> </FONT> ?。糂R><BR> ?。糉ORM METHOD="post" ACTION="/shoppingcart/CustomerServlet"> ?。糡ABLE WIDTH=450 CELLSPACING="0" CELLPADDING="0" BORDER="1"> ?。糡R> <TD WIDTH=10% BGCOLOR="#ECA613"> <B>姓名:</B></TD> ?。糡D WIDTH=90%> <INPUT TYPE=text SIZE=50 NAME="name" ></TD> ?。?TR> <TR> ?。糡D WIDTH=10% BGCOLOR="#ECA613"> <B>EMail: </B></TD> <TD WIDTH=90%> <INPUT TYPE=text SIZE=50 NAME="email"></TD> ?。?TR> ?。糡R> ?。糡D WIDTH=10% BGCOLOR="#ECA613" > <B>地址</B></TD> ?。糡D WIDTH=90%> <INPUT TYPE=text SIZE=50 NAME="address"></TD> ?。?TR> ?。糡R> <TD WIDTH=10% BGCOLOR="#ECA613"> <B>電話</B> </TD> ?。糡D WIDTH=90%> <INPUT TYPE=text SIZE=50 NAME="phone"></TD> </TR> ?。?TABLE> ?。? BasketBean basket = (BasketBean)session.getAttribute(BasketBean.BASKET); %> ?。糂> 總共價(jià)格人民幣<%= basket.getStringifiedValue(basket.getTotal()) %>元 ?。?B> ?。糂R> ?。糂R> ?。糡ABLE WIDTH=450 CELLSPACING="0" CELLPADDING="0" BORDER="0"> ?。糡R> <TD ALIGN=left><INPUT TYPE=reset VALUE="Reset"></TD> ?。糡D ALIGN=right><INPUT TYPE=submit NAME=<%= BasketBean.PAGE %> VALUE=<%= BasketBean.RECEIPT %> ></TD> </TR> ?。?TABLE> ?。?FORM> ?。?CENTER> ?。糂R><BR> ?。?@ include file="footer.html" %> </body> ?。?html> 代碼段5:Confirm.jsp <!-- Confirm.jsp - 確認(rèn)顧客購買商品,并且顯示賬單 --> ?。糷tml> ?。?-- 頁眉 --> ?。?@ include file="header.html" %> ?。糂ODY BGCOLOR="#FFFFF"> ?。糃ENTER> ?。糡ITLE> Grocery Joe's</TITLE> <FONT SIZE="+1"> ?。糂>Receipt</B> ?。?FONT> ?。?-- 使用Page指令導(dǎo)入頁面 --> <%@ page import="shoppingcart.*" errorPage="error.jsp" %> ?。?-- 聲明ReceiptBean的實(shí)例,用從request流中的數(shù)據(jù)設(shè)置屬性 --> ?。糺sp:useBean id="receipt_bean" scope="request" class="shoppingcart.ReceiptBean" /> <%-- ?。糺sp:setProperty name="receipt_bean" property="name" param="name" /> ?。糺sp:setProperty name="receipt_bean" property="email" param="email" /> <jsp:setProperty name="receipt_bean" property="street1" param="address" /> ?。糺sp:setProperty name="receipt_bean" property="phone" param="phone" /> --%> ?。糺sp:setProperty name="receipt_bean" property="*" /> ?。?jsp:useBean> ?。?-- 創(chuàng)建一個(gè)表,輸出顧客信息,并列出賬單。 --> <BR><BR> ?。糡ABLE WIDTH=450 CELLSPACING="0" CELLPADDING="0" BORDER="0"> ?。糡R> ?。糡D WIDTH=100%> <B>姓名: ?。糺sp:getProperty name="receipt_bean" property="name"/></B></TD> ?。?TR> <TR> ?。糡D WIDTH=100%> <B>EMail: ?。糺sp:getProperty name="receipt_bean" property="email"/></B></TD> ?。?TR> <TR> ?。糡D WIDTH=100% > <B>地址: ?。糺sp:getProperty name="receipt_bean" property="address"/></B></TD> ?。?TR> ?。糡R> ?。糡D WIDTH=100%> <B>電話: ?。糺sp:getProperty name="receipt_bean" property="phone"/></B></TD> ?。?TR> ?。?TABLE> ?。?-- 顯示出共花去多少錢 --> ?。? BasketBean basket = (BasketBean)session.getAttribute(BasketBean.BASKET); %> ?。糂> 總共價(jià)格人民幣<%= basket.getStringifiedValue(basket.getTotal()) %>元 </B> <% basket.clear(); %> ?。糂R> ?。糂R> ?。?CENTER> <BR><BR> ?。?@ include file="footer.html" %> ?。?body> ?。?html> 代碼六:Error.jsp ?。糎TML> ?。糎EAD> ?。糡ITLE> 錯(cuò)誤處理頁面 </TITLE> </HEAD> ?。?@ page isErrorPage="true" %> ?。糂ODY BGCOLOR=Black TEXT="#FFFFFF" LINK="#FFFFFF" VLINK="#FFFFFF"> <H1> 發(fā)生了一個(gè)錯(cuò)誤... </H1> ?。糂R><BR><BR> 是: <BR> ?。?= exception.toString() %> <BR> ?。?BODY> ?。?HTML> 現(xiàn)實(shí)中的應(yīng)用程序模型 前面我們介紹的這個(gè)程序,只是一個(gè)簡單的應(yīng)用程序,僅供大家參考。然而,一個(gè)真實(shí)的應(yīng)用程序其實(shí)和我們介紹的這個(gè)簡單應(yīng)用程序一樣遵循著 MVC模式 ;讓我們來看一看如何修改這個(gè)程序的某方面,來創(chuàng)造出一個(gè)更有現(xiàn)實(shí)意義的電子商務(wù)應(yīng)用程序。我們的電子商店應(yīng)用程序通過使用 BasketBean 類來實(shí)現(xiàn)了它的模型(Model),這存在著一些問題:它沒能定義一個(gè)標(biāo)準(zhǔn)的接口,這樣的缺陷限制了它的可維護(hù)性,可擴(kuò)展性和作為一個(gè)應(yīng)用程序的可伸縮性。 應(yīng)用程序應(yīng)該為訪問應(yīng)用程序模型定義一個(gè)標(biāo)準(zhǔn)的接口,接口實(shí)際上是建立一個(gè)約定,允許不同的實(shí)現(xiàn)應(yīng)當(dāng)按照要求被“插入(plugged-in)”,“可插入”的實(shí)現(xiàn)形式可以用橋模式來說明,橋模式的目的就是從功能的任何特定實(shí)現(xiàn)形式中分離出抽象的功能。例如,我們的存貨清單數(shù)據(jù)最初是作為靜態(tài)的信息被內(nèi)嵌到Java 代碼中的(見附錄一InventoryBean.java),為了獲得程序的靈活性,我們可以把數(shù)據(jù)從代碼中提取出來,并在文件系統(tǒng)上存儲它;而隨著數(shù)據(jù)體積的不斷膨脹,一個(gè)最直接的想法就是把數(shù)據(jù)轉(zhuǎn)移到一個(gè)關(guān)系型數(shù)據(jù)庫( RDBMS )中存儲,如果我們的 BasketBean 實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)的接口,那么我們就能重新實(shí)現(xiàn)這個(gè)接口來使用一個(gè)文件系統(tǒng)或一個(gè)關(guān)系型數(shù)據(jù)庫,而不用重寫 CustomerServlet類。因此,實(shí)際的應(yīng)用程序要求從代碼中分離數(shù)據(jù),數(shù)據(jù)是經(jīng)常變化的,但是代碼應(yīng)該盡可能少的變化,為能夠把我們的應(yīng)用程序移植到一個(gè)商務(wù)平臺上,最小的要求就是把它分割成的數(shù)據(jù)存取層和數(shù)據(jù)管理層。兩層結(jié)構(gòu)允許在不影響代碼的情況下增加數(shù)據(jù)。 ![]() 上圖顯示了在把數(shù)據(jù)與數(shù)據(jù)存取分開后并在定義一個(gè)標(biāo)準(zhǔn)的接口的情況下如何進(jìn)行程序設(shè)計(jì)的流程圖。 有時(shí)候,數(shù)據(jù)的可伸縮性和數(shù)據(jù)交易的要求迫使我們涉及數(shù)據(jù)管理體系的第三級結(jié)構(gòu),CORBA 或企業(yè)版JavaBean(EJB)提供的數(shù)據(jù)管理服務(wù)接口現(xiàn)在已經(jīng)被普便使用了,如果我們的BasketBean實(shí)現(xiàn)了一個(gè)標(biāo)準(zhǔn)的接口,那么我們就能把它作為一種分布式的服務(wù)來重新實(shí)現(xiàn)它。 下圖顯示了我們應(yīng)用程序模型的三層實(shí)現(xiàn)形式。 ![]() 寬松組件連接 JSP應(yīng)用程序能遵從 MVC 模式的原因之一就是這個(gè)模式支持清楚地定義model,view和controller組件所扮演的角色,我們應(yīng)該保持這些部件之間盡可能的寬松的聯(lián)合。 然而,我們并沒有使 CustomerServlet類寬松聯(lián)合,請看下面的語句 file://取得當(dāng)前的工作流程的狀態(tài)。 RequestDispatcher rd = null; String state = request.getParameter( BasketBean.STATE); if (state == null || state.equals(BasketBean.UPDATE)) { // 從商品目錄中選擇 rd = getServletConfig().getServletContext().getRequestDispatcher( "/jsp/Inventory.jsp"); } 控制器(controller)和表現(xiàn)形式(view)組件之間的緊密連接,使程序中如果改變一個(gè)組件就會改變相應(yīng)其他的組件。在我們的例子中,如果我們想把附加的JSP頁面添加到購物車工作流程中,我們也必須把附加的條件添加到 CustomerServlet中,這樣怎能談得上高可維護(hù)性和高伸縮性呢?如果我們能去掉CustomerServlet類和它的JSP頁面之間的緊密連接,我們的應(yīng)用程序就會有更多的可維護(hù)性和可伸縮性。要使這種緊密連接減小到最少程度的一個(gè)方法就是為每一個(gè)JSP頁面都創(chuàng)造一個(gè)助手Bean,我們在 CustomerServlet 中安裝這些助手Bean來管理所有的相關(guān)聯(lián)的 JSP 頁到來的HTML請求。 象這樣封裝每一個(gè)對象中的請求的模式是一種命令模式(command pattern)。 ![]() 就象橋模式(bridge pattern)一樣,實(shí)現(xiàn)一個(gè)命令模式的關(guān)鍵是聲明每個(gè)請求處理器必須實(shí)現(xiàn)的通用的接口。在我們的例子中,這個(gè)接口的最簡單的形式可能是一個(gè)單獨(dú)的方法讓我們傳遞請求參數(shù)和BasketBean對象—例如redirect()。因?yàn)榻涌诘拿恳环N具體的實(shí)現(xiàn)形式支持這個(gè)方法, CustomerServlet 就能在任何給定的處理機(jī)制上,在不知道實(shí)現(xiàn)形式的任何特定信息的情況下調(diào)用接口,我們可以為每一個(gè)JSP頁設(shè)定助手Bean,并且根據(jù)需要使它復(fù)雜化,例如,它能夠驗(yàn)證request發(fā)來的數(shù)據(jù)參數(shù),不管是簡單的判斷輸入的是否為空值還是更復(fù)雜的任務(wù)象驗(yàn)證信用卡信息等等。 一個(gè)JSP頁面只有一個(gè)輸入,但是它卻有多個(gè)輸出,這取決于提交按鈕的數(shù)量,每個(gè)輸出都能與一個(gè)不同的JSP頁相聯(lián)系。例如,Inventory.jsp 有兩個(gè)輸出,一個(gè)指向Purchase.jsp,另一個(gè)指回自己,我們能使用一個(gè)隱藏標(biāo)簽把每個(gè)助手Bean和輸出聯(lián)系起來。在 Inventory.jsp 中,我們可以使用 ?。糡D ALIGN=left> <INPUT TYPE=hidden NAME=<%= BasketBean.UPDATE %> VALUE="shoppingcart.UpdateHandler"> ?。糏NPUT TYPE=submit NAME=<%= BasketBean.PAGE %> VALUE=<%= BasketBean.UPDATE %>> </TD> 代替下面的行: ?。糡D ALIGN=left> ?。糏NPUT TYPE=submit NAME=<%= BasketBean.PAGE %> VALUE=<%= BasketBean.UPDATE %>> ?。?TD> JavaBean“shoppingcart.UpdateHandler”將被我們的CustomerServlet類實(shí)例化,它包括一個(gè) CustomerServlet 將調(diào)用的重定向方法。UpdateHandler 會知道怎么驗(yàn)證參數(shù),更新模型,并且把請求提交給相應(yīng)的JSP頁,因此這是一條完善CustomerServlet的編程的非常好的途徑。 小結(jié) 這樣一來,JSP就能極大的擴(kuò)展了 servlet 技術(shù),由于支持 Java 腳本,JSP和Servlet就使網(wǎng)頁設(shè)計(jì)師能夠隨心所欲的開發(fā)出高品質(zhì)的動態(tài)網(wǎng)頁程序。但不管怎么說,JSP也是不會代替 servlets的,因?yàn)閟ervlets,JSP和JavaBeans在網(wǎng)絡(luò)體系結(jié)構(gòu)中起著互相補(bǔ)足的作用。通過遵從MVC模式, JSP應(yīng)用程序能夠獨(dú)立地?cái)U(kuò)大或提高用于后臺控制的servlet,JSP頁面和現(xiàn)實(shí)的應(yīng)用程序模型。應(yīng)用程序模型能被擴(kuò)展到一個(gè)兩層或三層的方案,并且增加助手Bean,這都能管理JSP工作流程和支持應(yīng)用程序組件之間的寬松聯(lián)接。 附錄一、 InventoryBean.java類(下面我將不會對代碼進(jìn)行過多的解釋) package shoppingcart; public class InventoryBean { /* 商品名稱 */ private static final String[] names = { "牙膏", "肥皂", "毛巾", "口香糖", "牙刷", "練習(xí)本", "鋼筆"}; /* 商品序列號。 */ private static final int[] skus = { 1, 2, 3, 4, 5, 6, 7 }; /* 商品每件的價(jià)格。 */ private static final double[] prices = { 2.50, 3.90, 5.25, 1.00, 3.50, 0.40, 11.80}; private Product[] catalogue_ = null; private static InventoryBean inventory_ = new InventoryBean(); private InventoryBean() { catalogue_ = new Product[skus.length]; for (int i = 0; i < skus.length; i++) { catalogue_[i] = createProduct(skus[i]); } } public static Product[] getCatalogue() { return inventory_.catalogue_; } private Product createProduct(int sku) { return new Product(sku, names[sku-1], prices[sku-1]); } } 附錄二、Product類 package shoppingcart; public class Product implements Cloneable { private int sku_ = 0; private String name_ = null; private double price_ = 0; private int Pieces_ = 0; public String toString() { return "商品序列號: " + sku_ + ", 商品名: " + name_ + ", 單價(jià): " + price_ + ", 件數(shù): " + pieces_; } public Product(int sku, String name, double price) { sku_ = sku; name_ = name; price_ = price; } public int getSKU() { return sku_; } public String getName() { return name_; } public double getPrice() { return price_; } public double getPieces() { return pounds_; } public void setPieces(int pieces) { pieces_ = pieces; } public Object clone() { Object dup = null; try { up = super.clone(); } catch(CloneNotSupportedException ignore) { } return dup; } } 附錄三、ReceiptBean類 package shoppingcart; import javax.servlet.*; import javax.servlet.http.*; public class ReceiptBean { private String name_ = null; private String email_ = null; private String address_ = null; private String phone_ = null; public ReceiptBean() { } public void setName(String name) { name_ = name; } public String getName() { return name_; } public void setEmail(String email) { email_ = email; } public String getEmail() { return email_; } public void setAddress(String address) { address_ = address; } public String getAddress() { return address_; } public void setPhone(String phone) { phone_ = phone; } public String getPhone() { return phone_; } } |