国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JSP > 正文

理解jsp模式2架構(gòu):MVC設(shè)計(jì)模式探險(xiǎn)

2024-09-05 00:20:14
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

  摘要:通過(guò)開發(fā)一個(gè)熟悉的基于web的購(gòu)物店,你將學(xué)到如何工具化mvc設(shè)計(jì)模式并且真正地在使用jsp的時(shí)候分離內(nèi)容和表現(xiàn)。govind seshadri 會(huì)向你展示這是多么的容易(2000字(原文字?jǐn)?shù)))。

  盡管相對(duì)拋開最近的相關(guān)介紹而言,jsp技術(shù)正在很好地以自己的方式成為卓越的創(chuàng)建提供動(dòng)態(tài)web內(nèi)容的應(yīng)用程序的java技術(shù)。java開發(fā)者因?yàn)樵S多不同的理由喜愛jsp。一些人喜歡它給交互式web頁(yè)面帶來(lái)了“一次編寫,到處運(yùn)行”的變化這個(gè)事實(shí);另一些人欣賞它易學(xué)易用并且?guī)椭藗儼裫ava作為服務(wù)器端腳本使用。但是都公認(rèn)一件事——使用jsp最大的好處是能夠有效地分離內(nèi)容與表現(xiàn)。在這篇文章里,我來(lái)提供一個(gè)深入的看法,關(guān)于如何使用jsp模式2架構(gòu)獲得最佳的內(nèi)容與表現(xiàn)的分離。這個(gè)模式也可以被看作流行的mvc設(shè)計(jì)模式在服務(wù)端的實(shí)現(xiàn)。請(qǐng)注意在開始之前你應(yīng)該熟悉jsp和servlet編程,因?yàn)槲也粫?huì)在這篇文章中討論語(yǔ)法問題。

  那么,servlet有什么問題?

  既然jsp用來(lái)提供動(dòng)態(tài)web內(nèi)容并且對(duì)于從表現(xiàn)層中分離內(nèi)容很不錯(cuò),一些人也許想知道為什么servlet要從jsp中脫離出來(lái)與它并列。servlet的功用沒有問題。它對(duì)于服務(wù)端處理干得很好,而且,由于它重要的已安裝基礎(chǔ),就適合這個(gè)。實(shí)際上,從結(jié)構(gòu)上說(shuō),你可以把jsp看作實(shí)現(xiàn)為servlet 2.1 api的擴(kuò)展的servlet高級(jí)抽象。仍然不應(yīng)該不加區(qū)別地使用servlet;它可能不會(huì)適用于每一個(gè)人。舉個(gè)例子來(lái)說(shuō),盡管頁(yè)面設(shè)計(jì)者能夠很容易地使用常規(guī)html或者xml工具編寫jsp頁(yè)面,而servlet通常更適合后臺(tái)開發(fā)者,他們通常使用某種ide——一個(gè)通常需要高層次的編程專門知識(shí)的過(guò)程。當(dāng)發(fā)布servlet時(shí),即使開發(fā)者也必須留意和確認(rèn)在內(nèi)容和表現(xiàn)之間沒有緊耦合。通常,你可以通過(guò)加入第三方的html封裝包比如htmlkona來(lái)做這個(gè)。即使這樣做了,盡管帶來(lái)了一些簡(jiǎn)單的對(duì)于屏幕變化的伸縮性,仍然不能為你防止免受表現(xiàn)格式自身的變化的影響。例如,如果你的表現(xiàn)形式從html轉(zhuǎn)變到dhtml,你將仍然需要確認(rèn)你的封裝包是否兼容這種新格式。在最壞的情況下,如果封裝包不能用了,你可能最終會(huì)在動(dòng)態(tài)內(nèi)容內(nèi)部硬編碼表現(xiàn)形式。那么,解決辦法是什么?就像你你將要看到的,一個(gè)辦法將會(huì)同時(shí)使用jsp和servlet來(lái)創(chuàng)建應(yīng)用系統(tǒng)。

  差異哲學(xué)

  早期的jsp規(guī)范主張兩種使用jsp技術(shù)創(chuàng)建應(yīng)用的哲學(xué)思路。這兩種思路,用術(shù)語(yǔ)來(lái)說(shuō)就是jsp模式1和模式2,本質(zhì)上的區(qū)別在于大部分請(qǐng)求的處理發(fā)生的位置。在模式1架構(gòu)中,如圖1所示,jsp頁(yè)面獨(dú)立地負(fù)責(zé)處理請(qǐng)求和發(fā)送反饋給客戶端。這里仍然有內(nèi)容和表現(xiàn)的分離,因?yàn)樗械臄?shù)據(jù)訪問是使用bean完成的。盡管模式1架構(gòu)應(yīng)該很適合簡(jiǎn)單應(yīng)用,但是對(duì)于復(fù)雜的實(shí)現(xiàn)是不可取的。這種結(jié)構(gòu)的任意使用通常會(huì)導(dǎo)致大量的腳本和java代碼嵌入到j(luò)sp頁(yè)面中,特別是在有大量的請(qǐng)求需要處理的情況下。盡管這可能對(duì)java開發(fā)者來(lái)說(shuō)不是一個(gè)大問題,但是卻無(wú)疑是一個(gè)問題,如果你的jsp頁(yè)面是由設(shè)計(jì)師創(chuàng)建和維護(hù)的話——在大項(xiàng)目中通常如此。最終,這個(gè)問題甚至?xí)?dǎo)致角色定義和責(zé)任分配的混亂,引起本可以輕松避免的項(xiàng)目管理的麻煩。


圖1:jsp模式1結(jié)構(gòu)

  模式2架構(gòu)如圖2所示,是一個(gè)為動(dòng)態(tài)內(nèi)容服務(wù)的混合方案,因?yàn)樗瑫r(shí)使用了servlet和jsp。它利用了兩種技術(shù)的優(yōu)勢(shì),使用jsp產(chǎn)生表現(xiàn)層而servlet負(fù)責(zé)執(zhí)行敏感任務(wù)。在這里,servlet扮演控制器的角色,負(fù)責(zé)請(qǐng)求處理和產(chǎn)生jsp要使用的bean和對(duì)象,以及根據(jù)客戶的動(dòng)作決定下一步轉(zhuǎn)發(fā)到哪一個(gè)jsp頁(yè)面。特別要注意的是jsp頁(yè)面內(nèi)部并沒有處理邏輯;它只是簡(jiǎn)單地負(fù)責(zé)取得可能是servelet事先創(chuàng)建的對(duì)象和bean,并為在了靜態(tài)模版中插入從servlet釋放出動(dòng)態(tài)內(nèi)容。我的觀點(diǎn)是,這個(gè)辦法一般會(huì)形成最干凈徹底的表現(xiàn)與內(nèi)容的分離,使得你的開發(fā)團(tuán)隊(duì)里的開發(fā)者和頁(yè)面設(shè)計(jì)師的角色與責(zé)任能夠清晰。實(shí)際上,你的應(yīng)用越復(fù)雜,使用模式2帶來(lái)的好處就越多。


圖2:jsp模式2結(jié)構(gòu)

  為了弄清模式2背后的概念,我們來(lái)參觀一個(gè)細(xì)化的具體實(shí)現(xiàn):一個(gè)叫做音樂無(wú)界的在線音樂商店樣品。

  了解音樂無(wú)界

  主視圖,或者說(shuō)表現(xiàn)層,對(duì)于我們的音樂無(wú)界由jsp頁(yè)面eshop.jsp產(chǎn)生(見清單1)。你會(huì)注意到這個(gè)頁(yè)面幾乎僅僅處理這個(gè)應(yīng)用的主要用戶界面,而且沒有做任何處理工作——一個(gè)最佳的jsp腳本。也注意一下另一個(gè)jsp頁(yè)面,cart.jsp(見清單2),通過(guò)指令<jsp:include page="cart.jsp" flush="true" />包含在eshop.jsp之內(nèi)。

清單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>

 

清單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>
    <% } %>
  </table>
  <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處理基于session的購(gòu)物車的表現(xiàn)形式,它指定了我們的mvc結(jié)構(gòu)中的模型。觀察cart.jsp開頭這一段腳本:
<%
  vector buylist = (vector) session.getvalue("shopping.shoppingcart");
  if (buylist != null && (buylist.size() > 0)) {
%>
  基本上,這段腳本從session中提出了購(gòu)物車。如果購(gòu)物車為空或者還未創(chuàng)建,它不會(huì)顯示任何東西;因此,當(dāng)用戶第一次訪問的時(shí)候,他見到的頁(yè)面如圖3。


圖3:音樂無(wú)界,主視圖

  如果購(gòu)物車不是空的,那么已選中的物品會(huì)一次一個(gè)地從購(gòu)物車中被提出,像下面的腳本示范的那樣:

<%
  for (int index=0; index < buylist.size(); index++) {
    cd anorder = (cd) buylist.elementat(index);
%>
  一旦描述物品的變量已創(chuàng)建,它們就簡(jiǎn)單地被jsp表達(dá)式插入到靜態(tài)html模版中去。圖4顯示了用戶已經(jīng)放了一些東西到購(gòu)物車?yán)锶ナ堑那闆r。


圖4:音樂無(wú)界,購(gòu)物車視圖

  這里要注意的重要的一件事是對(duì)所有動(dòng)作的處理既不發(fā)生在eshop.jsp也不在cart.jsp里,而是由控制器servlet,shoppingservlet.java處理,見清單3:

清單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中添加一件物品,請(qǐng)求都被發(fā)送到這個(gè)控制器servlet。它依次決定合適的動(dòng)作,然后處理相應(yīng)要添加的物品的請(qǐng)求參數(shù)。它就實(shí)例化一個(gè)新的cd bean(見清單4)代表這個(gè)選擇的物品,接著在把這個(gè)bean放進(jìn)session之前處理購(gòu)物車的更新。

清單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;
  }
}

  注意我們?cè)谶@個(gè)servlet中還包括了額外的智能,因此它能夠知道如果選擇了一張已在購(gòu)物車中的cd,那么應(yīng)該簡(jiǎn)單地增加session中cd bean的計(jì)數(shù)。它也處理從cart.jsp中觸發(fā)的動(dòng)作,比如用戶從購(gòu)物車中刪除物品,或是繼續(xù)去收銀臺(tái)結(jié)帳。注意控制器總是對(duì)哪個(gè)資源應(yīng)該被調(diào)用來(lái)對(duì)特定的動(dòng)作產(chǎn)生回饋有完全的控制權(quán)。例如,對(duì)購(gòu)物車狀態(tài)的改變,像增加和刪除,會(huì)引起控制器將請(qǐng)求處理后轉(zhuǎn)發(fā)給eshop.jsp頁(yè)面。這樣引起該頁(yè)面依照已更新的購(gòu)物車依次重新顯示主視圖。如果用戶決定結(jié)帳,則請(qǐng)求被處理后轉(zhuǎn)發(fā)給checkout.jsp(見清單5),通過(guò)后面的請(qǐng)求分配器,象下面顯示的這樣:

string url="/jsp/shopping/checkout.jsp";
servletcontext sc = getservletcontext();
requestdispatcher rd = sc.getrequestdispatcher(url);
rd.forward(req,res);

清單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>
</table>
<p>
<a href="/examples/jsp/shopping/eshop.jsp">shop some more!</a>
</center>
</body>
</html>

  checkout.jsp僅僅從session中提出購(gòu)物車并為此請(qǐng)求提取出總金額,然后顯示選中的物品和他們的總價(jià)格。圖5顯示了結(jié)算時(shí)的用戶視圖。一旦用戶去結(jié)帳,刪除session對(duì)象是同樣重要的。這個(gè)由頁(yè)面末端的session.invalidate()調(diào)用來(lái)完成。有兩個(gè)理由必須這樣做。第一,如果沒有使session無(wú)效,用戶的購(gòu)物車不會(huì)重新初始化;如果用戶結(jié)帳后試圖開始新一輪的采購(gòu),她的購(gòu)物車會(huì)繼續(xù)保存著已經(jīng)付過(guò)錢的物品。第二,如果用戶結(jié)帳后僅僅是離開了網(wǎng)站,這個(gè)session對(duì)象不會(huì)被垃圾收集機(jī)制回收而是繼續(xù)占用寶貴的系統(tǒng)資源直到租約到期。因?yàn)槿笔〉膕ession租約時(shí)間是大約三十分鐘,在一個(gè)大容量系統(tǒng)上這將會(huì)很快導(dǎo)致系統(tǒng)內(nèi)存耗盡。當(dāng)然,我們都知道對(duì)一個(gè)耗盡了系統(tǒng)內(nèi)存的應(yīng)用程序會(huì)發(fā)生什么。


圖5:結(jié)賬視圖

  注意這個(gè)應(yīng)用所有的資源都是session相關(guān)的,因?yàn)檫@里的模式存儲(chǔ)在session里。因此,你必須確保用戶不會(huì)因?yàn)槟承┰蛏踔劣捎阱e(cuò)誤直接訪問控制器。你可以在控制器檢測(cè)到缺少有效session的時(shí)候讓客戶端自動(dòng)轉(zhuǎn)向到一個(gè)錯(cuò)誤頁(yè)面(見列表6),來(lái)避免這種情況的發(fā)生。

列表6

error.html

<html>
<body>
<h1>
  sorry, there was an unrecoverable error! <br>
  please try <a href="/examples/jsp/shopping/eshop.jsp">again</a>.
</h1>
</body>
</html>

  部署音樂無(wú)界

  我假定你正在使用來(lái)自sun的最新版本的javaserver web development kit (jswdk)來(lái)運(yùn)行這個(gè)例子。如果不是,參看資源小節(jié)去看看到哪里取得它。假設(shè)服務(wù)器安裝在/jswdk-1.0.1,這是microsoft windows系統(tǒng)下的缺省路徑,可以象下面這樣部署音樂無(wú)界應(yīng)用:

create shopping directory under /jswdk-1.0.1/examples/jsp
copy eshop.jsp to /jswdk-1.0.1/examples/jsp/shopping
copy cart.jsp to /jswdk-1.0.1/examples/jsp/shopping
copy checkout.jsp to /jswdk-1.0.1/examples/jsp/shopping
compile the .java files by typing javac *.java
copy shoppingservlet.class to /jswdk-1.0.1/webpages/web-inf/servlets
create shopping directory under /jswdk-1.0.1/examples/web-inf/jsp/beans
copy cd.class to /jswdk-1.0.1/examples/web-inf/jsp/beans/shopping
copy error.html to /jswdk-1.0.1/webpages
once your server has been started, you should be able to access the application using http://localhost:8080/examples/jsp/shopping/eshop.jsp as the url
只要你的服務(wù)器啟動(dòng),你應(yīng)該可以使用url http://localhost:8080/examples/jsp/shopping/eshop.jsp來(lái)訪問這個(gè)應(yīng)用程序。

  利用jsp和servlet

  著這個(gè)例子里,我們從細(xì)節(jié)上檢查了控制的層次和模式2架構(gòu)提供的靈活性。實(shí)際上,我們已經(jīng)看到了servlet和jsp頁(yè)面最好的特性是如何開發(fā)出最大化的內(nèi)容與表現(xiàn)的剝離。只要正確地應(yīng)用,模式2架構(gòu)會(huì)將所有的處理邏輯集中到控制器servlet手里,而jsp頁(yè)面只負(fù)責(zé)視圖或者說(shuō)表現(xiàn)層的工作。然而,使用模式2結(jié)構(gòu)的阻力在于它的復(fù)雜性。因此,對(duì)于簡(jiǎn)單應(yīng)用使用模式1也是可以接受的。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 合山市| 皮山县| 错那县| 威远县| 阳城县| 绩溪县| 神池县| 榆林市| 无为县| 南华县| 眉山市| 云南省| 宣恩县| 玛纳斯县| 北海市| 财经| 苏尼特右旗| 寿宁县| 遂昌县| 韶山市| 柳河县| 句容市| 依安县| 固镇县| 娱乐| 湘阴县| 深水埗区| 莱西市| 阿勒泰市| 遂宁市| 赞皇县| 抚远县| 望谟县| 西城区| 古交市| 弥勒县| 光泽县| 禄丰县| 赣州市| 轮台县| 奉贤区|