JSP高級技術(shù)怎么開發(fā)動態(tài)網(wǎng)站
發(fā)表時間:2023-08-01 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]近年來,Jsp技術(shù)現(xiàn)在已經(jīng)成為一種卓越的動態(tài)網(wǎng)站開發(fā)技術(shù)。Java開發(fā)者出于各種理由喜愛使用jsp。有人喜愛其“一次開發(fā),處處使用”的性能,另外的人覺得jsp使java成為一種易學(xué)的服務(wù)器端scri...
近年來,Jsp技術(shù)現(xiàn)在已經(jīng)成為一種卓越的動態(tài)網(wǎng)站開發(fā)技術(shù)。Java開發(fā)者出于各種理由喜愛使用jsp。有人喜愛其“一次開發(fā),處處使用”的性能,另外的人覺得jsp使java成為一種易學(xué)的服務(wù)器端scripting語言。但是,jsp最大的長處在它將頁面的表現(xiàn)和頁面的商業(yè)邏輯分開了。本章中,我們將深入地討論如何使用jsp模式2體系結(jié)構(gòu)來開發(fā)網(wǎng)站。這一模式可以被看作是通用模式瀏覽控制模式(popular Model-View-Controller,MVC)模式的服務(wù)器端實(shí)現(xiàn)。
Servlets有何缺陷?
當(dāng)jsp成為開發(fā)動態(tài)網(wǎng)站的主要技術(shù)時,可能有人會問為何在jsp技術(shù)中我們不強(qiáng)調(diào)servlets。Servlets的應(yīng)用是沒有問題的。它們非常適于服務(wù)器端的處理和編程,并且它們會長期駐留在他們現(xiàn)在的位置。但是,從結(jié)構(gòu)上說,我們可以將jsp看作是servlet的一個高層的抽象實(shí)現(xiàn),特別是在servlet 2.2 API下。但是,你仍然不能無拘束地使用servlet;它們并不適合每一個人。例如,頁面設(shè)計(jì)者可以方便地使用html或者xml工具開發(fā)jsp頁面,但servlet卻更適合于后端開發(fā)者使用,他們的工具是ide——一個需要更多編程訓(xùn)練的開發(fā)領(lǐng)域。當(dāng)發(fā)布servlet時,每個開發(fā)者必須小心地確定在頁面表現(xiàn)和頁面邏輯之間沒有緊密的關(guān)聯(lián)出現(xiàn)。你可以使用第三方html包裝工具,如htmlkona來混合html和servlet代碼。即使如此,這點(diǎn)靈活性還不足以讓你自由地改變風(fēng)格本身。例如,你希望從html改變到dhtml,則包裝本身需要被小心地測試,以確保新的格式可以正確使用。在最壞的情況下,包裝不可用,你就需要應(yīng)變馬來表現(xiàn)動態(tài)內(nèi)容。所以,需要一種新的解決方案。你將會看到,一種方案就是混合jsp和servlet的使用。
不同的方式
早期的jsp標(biāo)準(zhǔn)給出了兩種使用jsp的方式。這些方式,都可以歸納為jsp模式1和jsp模式2,主要的差別在于處理大量請求的位置不同。在模式1中(圖1),jsp頁面獨(dú)自響應(yīng)請求并將處理結(jié)果返回客戶。這里仍然有表現(xiàn)和內(nèi)容的分離,因?yàn)樗械臄?shù)據(jù)依靠bean來處理。盡管模式1 可以很好的滿足小型應(yīng)用的需要,但卻不能滿足大型應(yīng)用的要求。大量使用模式1,常常會導(dǎo)致頁面被嵌入大量的script或者java代碼。特別是當(dāng)需要處理的商業(yè)邏輯很復(fù)雜時,情況會變得嚴(yán)重。也許對于java程序員來說,這不算大的問題。但如果開發(fā)者是前端界面設(shè)計(jì)人員——在大型項(xiàng)目中,這非常常見,——則代碼的開發(fā)和維護(hù)將出現(xiàn)困難。在任何項(xiàng)目中,這樣的模式多少總會導(dǎo)致定義不清的響應(yīng)和項(xiàng)目管理的困難。
在圖2中顯示的模式2 結(jié)構(gòu),是一種面向動態(tài)內(nèi)容的實(shí)現(xiàn),結(jié)合了servlet 和jsp技術(shù)。它利用了兩種技術(shù)原有的優(yōu)點(diǎn),采用jsp來表現(xiàn)頁面,采用servlets來完成大量的處理。這里,servlet扮演一個控制者的角色,并負(fù)責(zé)響應(yīng)客戶請求。接著,servlet創(chuàng)建jsp需要的bean和對象,再根據(jù)用戶的行為,決定將那個jsp頁面發(fā)送給用戶。特別要注意,jsp頁面中沒有任何商業(yè)處理邏輯;它只是簡單地檢索servlet先前創(chuàng)建的bean 或者對象,再將動態(tài)內(nèi)容插入預(yù)定義的模版。從開發(fā)的觀點(diǎn)看,這一模式具有更清晰的頁面表現(xiàn),清楚的開發(fā)者角色劃分,可以充分地利用開發(fā)小組中的界面設(shè)計(jì)人員。事實(shí)上,越是復(fù)雜的項(xiàng)目,采用模式2 的好處就越突出。
為了清楚地了解模式2 的開發(fā)過程,我們舉一個網(wǎng)上音樂商店的例子。
我們創(chuàng)建一個叫”音樂無國界”的銷售音樂制品的商店!耙魳窡o國界”在線商店的主界面,是一個叫“音樂無國界”的頁面(代碼1)。你會看到,這個頁面完全著眼于用戶界面,與處理邏輯無關(guān)。另外,注意另外一個jsp頁面,Cart.jsp(在代碼2中),用<jsp:include page="Cart.jsp" flush="true" />.嵌入Eshop.jsp中。
Listing 1:
EShop.jsp
<%@ page session="true" %>
<html>
<head>
<title>Music Without Borders</title>
</head>
<body bgcolor="#33CCFF">
<font face="Times New Roman,Times" size="+3">
Music Without Borders
</font>
<hr><p>
<center>
<form name="shoppingForm"
action="/examples/servlet/ShoppingServlet"
method="POST">
<b>CD:</b>
<select name=CD>
<option>Yuan The Guo Brothers China $14.95</option>
<option>Drums of Passion Babatunde Olatunji Nigeria $16.95</option>
<option>Kaira Tounami Diabate Mali $16.95</option>
<option>The Lion is Loose Eliades Ochoa Cuba $13.95</option>
<option>Dance the Devil Away Outback Australia $14.95</option>
<option>Record of Changes Samulnori Korea $12.95</option>
<option>Djelika Tounami Diabate Mali $14.95</option>
<option>Rapture Nusrat Fateh Ali Khan Pakistan $12.95</option>
<option>Cesaria Evora Cesaria Evora Cape Verde $16.95</option>
<option>Ibuki Kodo Japan $13.95</option>
</select>
<b>Quantity: </b><input type="text" name="qty" SIZE="3" value=1>
<input type="hidden" name="action" value="ADD">
<input type="submit" name="Submit" value="Add to Cart">
</form>
</center>
<p>
<jsp:include page="Cart.jsp" flush="true" />
</body>
</html>
Listing 2:
Cart.jsp
<%@ page session="true" import="java.util.*, shopping.CD" %>
<%
Vector buylist = (Vector) session.getValue("shopping.shoppingcart");
if (buylist != null && (buylist.size() > 0)) {
%>
<center>
<table border="0" cellpadding="0" width="100%" bgcolor="#FFFFFF">
<tr>
<td><b>ALBUM</b></td>
<td><b>ARTIST</b></td>
<td><b>COUNTRY</b></td>
<td><b>PRICE</b></td>
<td><b>QUANTITY</b></td>
<td></td>
</tr>
<%
for (int index=0; index < buylist.size();index++) {
CD anOrder = (CD) buylist.elementAt(index);
%>
<tr>
<td><b><%= anOrder.getAlbum() %></b></td>
<td><b><%= anOrder.getArtist() %></b></td>
<td><b><%= anOrder.getCountry() %></b></td>
<td><b><%= anOrder.getPrice() %></b></td>
<td><b><%= anOrder.getQuantity() %></b></td>
<td>
<form name="deleteForm"
action="/examples/servlet/ShoppingServlet"
method="POST">
<input type="submit" value="Delete">
<input type="hidden" name= "delindex" value='<%= index %>'>
<input type="hidden" name="action" value="DELETE">
</form>
</td>
</tr>
<% } %>
<p>
<form name="checkoutForm"
action="/examples/servlet/ShoppingServlet"
method="POST">
<input type="hidden" name="action" value="CHECKOUT">
<input type="submit" name="Checkout" value="Checkout">
</form>
</center>
<% } %>
這里,Cart.jsp按照MVC的模式1處理基于SESSION的購物車的表現(xiàn)。請看Cart.jsp開始處的代碼:
<%
Vector buylist = (Vector) session.getValue("shopping.shoppingcart");
if (buylist != null && (buylist.size() > 0)) {
%>
本質(zhì)上,這段代碼從SESSION中取出“購物車”。如果“購物車”為空或者沒有被創(chuàng)建,它就什么也不顯示。所以,在用戶第一次訪問應(yīng)用時,其界面如圖:
如果“購物車”不為空,用戶選擇的商品從車中取出,依次顯示在頁面上:
<%
for (int index=0; index < buylist.size(); index++) {
CD anOrder = (CD) buylist.elementAt(index);
%>
一旦生成一個物品的說明,就使用JSP按照事先設(shè)定的模板將其插入靜態(tài)HTML頁面。下圖顯示了用戶選購一些物品后的界面:
需要注意的一個重要的地方是所有關(guān)于Eshop.jsp,Cart.jsp的處理有一個控制SERVLET,ShoppingServlet.java,代碼在源程序3中:
Listing 3:
ShoppingServlet.java
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import shopping.CD;
public class ShoppingServlet extends HttpServlet {
public void init(ServletConfig conf) throws ServletException {
super.init(conf);
}
public void doPost (HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = req.getSession(false);
if (session == null) {
res.sendRedirect("http://localhost:8080/error.html");
}
Vector buylist=
(Vector)session.getValue("shopping.shoppingcart");
String action = req.getParameter("action");
if (!action.equals("CHECKOUT")) {
if (action.equals("DELETE")) {
String del = req.getParameter("delindex");
int d = (new Integer(del)).intValue();
buylist.removeElementAt(d);
} else if (action.equals("ADD")) {
//any previous buys of same cd?
boolean match=false;
CD aCD = getCD(req);
if (buylist==null) {
//add first cd to the cart
buylist = new Vector(); //first order
buylist.addElement(aCD);
} else { // not first buy
for (int i=0; i< buylist.size(); i++) {
CD cd = (CD) buylist.elementAt(i);
if (cd.getAlbum().equals(aCD.getAlbum())) {
cd.setQuantity(cd.getQuantity()+aCD.getQuantity());
buylist.setElementAt(cd,i);
match = true;
} //end of if name matches
} // end of for
if (!match)
buylist.addElement(aCD);
}
}
session.putValue("shopping.shoppingcart", buylist);
String url="/jsp/shopping/EShop.jsp";
ServletContext sc = getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher(url);
rd.forward(req, res);
} else if (action.equals("CHECKOUT")) {
float total =0;
for (int i=0; i< buylist.size();i++) {
CD anOrder = (CD) buylist.elementAt(i);
float price= anOrder.getPrice();
int qty = anOrder.getQuantity();
total += (price * qty);
}
total += 0.005;
String amount = new Float(total).toString();
int n = amount.indexOf('.');
amount = amount.substring(0,n+3);
req.setAttribute("amount",amount);
String url="/jsp/shopping/Checkout.jsp";
ServletContext sc = getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher(url);
rd.forward(req,res);
}
}
private CD getCD(HttpServletRequest req) {
//imagine if all this was in a scriptlet...ugly, eh?
String myCd = req.getParameter("CD");
String qty = req.getParameter("qty");
StringTokenizer t = new StringTokenizer(myCd," ");
String album= t.nextToken();
String artist = t.nextToken();
String country = t.nextToken();
String price = t.nextToken();
price = price.replace('$',' ').trim();
CD cd = new CD();
cd.setAlbum(album);
cd.setArtist(artist);
cd.setCountry(country);
cd.setPrice((new Float(price)).floatValue());
cd.setQuantity((new Integer(qty)).intValue());
return cd;
}
}
每次用戶用Eshop.jsp增加一個商品,頁面就請求控制SERVLET?刂芐ERVLET決定進(jìn)一步的行動,并處理增加的商品。接著,控制SERVLET實(shí)例化一個新的BEAN CD代表選定的商品,并在返回SESSION前更新購物車對象。
Listing 4:
CD.java
package shopping;
public class CD {
String album;
String artist;
String country;
float price;
int quantity;
public CD() {
album="";
artist="";
country="";
price=0;
quantity=0;
}
public void setAlbum(String title) {
album=title;
}
public String getAlbum() {
return album;
}
public void setArtist(String group) {
artist=group;
}
public String getArtist() {
return artist;
}
public void setCountry(String cty) {
country=cty;
}
public String getCountry() {
return country;
}
public void setPrice(float p) {
price=p;
}
public float getPrice() {
return price;
}
public void setQuantity(int q) {
quantity=q;
}
public int getQuantity() {
return quantity;
}
}
注意,我們的SERVLET中具有附加的智能,如果一個物品被重復(fù)選擇,不會增加新的記錄,而是在以前的記錄上更新計(jì)數(shù)。控制SERVLET也響應(yīng)Cart.jsp中的行為,如修改數(shù)量,刪除商品,還有結(jié)帳。如果結(jié)帳,控制通過下述語句轉(zhuǎn)向Checkout.jsp頁面(源程序5):
String url="/jsp/shopping/Checkout.jsp";
ServletContext sc = getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher(url);
rd.forward(req,res);
Listing 5:
Checkout.jsp
<%@ page session="true" import="java.util.*, shopping.CD" %>
<html>
<head>
<title>Music Without Borders Checkout</title>
</head>
<body bgcolor="#33CCFF">
<font face="Times New Roman,Times" size=+3>
Music Without Borders Checkout
</font>
<hr><p>
<center>
<table border="0" cellpadding="0" width="100%" bgcolor="#FFFFFF">
<tr>
<td><b>ALBUM</b></td>
<td><b>ARTIST</b></td>
<td><b>COUNTRY</b></td>
<td><b>PRICE</b></td>
<td><b>QUANTITY</b></td>
<td></td>
</tr>
<%
Vector buylist = (Vector) session.getValue("shopping.shoppingcart");
String amount = (String) request.getAttribute("amount");
for (int i=0; i < buylist.size();i++) {
CD anOrder = (CD) buylist.elementAt(i);
%>
<tr>
<td><b><%= anOrder.getAlbum() %></b></td>
<td><b><%= anOrder.getArtist() %></b></td>
<td><b><%= anOrder.getCountry() %></b></td>
<td><b><%= anOrder.getPrice() %></b></td>
<td><b><%= anOrder.getQuantity() %></b></td>
</tr>
<%
}
session.invalidate();
%>
<tr>
<td> </td>
<td> </td>
<td><b>TOTAL</b></td>
<td><b>$<%= amount %></b></td>
<td> </td>
</tr>
<p>
<a href="/examples/jsp/shopping/EShop.jsp">Shop some more!</a>
</center>
</body>
</html>
結(jié)帳頁面簡單地從SESSION中取出購物車,然后顯示每個物品和總金額。這里的關(guān)鍵是要結(jié)束SESSION,因此在頁面中有一個session.invalidate()調(diào)用。這一處理有兩個原因。首先,如果不結(jié)束SESSION,用戶的購物車不會被初始化,如果用戶要繼續(xù)購買,車中會保留他已經(jīng)支付過的商品。另外,如果用戶不結(jié)帳就離開了,則SESSION會繼續(xù)占用有效的資源直到過期。過期時間一般是30分鐘,在一個大的站點(diǎn)上,這樣的情況會很快導(dǎo)致資源耗盡。當(dāng)然,這是我們不愿看到的。
注意,所有的資源分配在這個例子中是基于SESSION的。所以,你必須確?刂芐ERVLET不被用戶訪問,即使是意外的訪問也不允許。這可以在控制檢查到一個非法訪問時用一個簡單的重定向錯誤頁面來處理。見源代碼6。
Listing 6:
error.html
<html>
<body>
<h1>
Sorry, there was an unrecoverable error!
Please try <a href="/examples/jsp/shopping/EShop.jsp">again</a>.
</h1>
</body>
</html>
小結(jié)
本章的討論顯示,使用模式2,JSP和SERVLET可以在功能上最大限度的分開。正確地使用模式2,導(dǎo)致一個中心化的控制SERVLET,以及只完成顯示的JSP頁面。另一方面,模式2的實(shí)現(xiàn)很復(fù)雜。因此,在簡單的應(yīng)用中,可以考慮使用模式1。