亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JSP > 正文

理解jsp模式2架構:MVC設計模式探險

2024-09-05 00:20:14
字體:
來源:轉載
供稿:網友

  摘要:通過開發一個熟悉的基于web的購物店,你將學到如何工具化mvc設計模式并且真正地在使用jsp的時候分離內容和表現。govind seshadri 會向你展示這是多么的容易(2000字(原文字數))。

  盡管相對拋開最近的相關介紹而言,jsp技術正在很好地以自己的方式成為卓越的創建提供動態web內容的應用程序的java技術。java開發者因為許多不同的理由喜愛jsp。一些人喜歡它給交互式web頁面帶來了“一次編寫,到處運行”的變化這個事實;另一些人欣賞它易學易用并且幫助人們把java作為服務器端腳本使用。但是都公認一件事——使用jsp最大的好處是能夠有效地分離內容與表現。在這篇文章里,我來提供一個深入的看法,關于如何使用jsp模式2架構獲得最佳的內容與表現的分離。這個模式也可以被看作流行的mvc設計模式在服務端的實現。請注意在開始之前你應該熟悉jsp和servlet編程,因為我不會在這篇文章中討論語法問題。

  那么,servlet有什么問題?

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

  差異哲學

  早期的jsp規范主張兩種使用jsp技術創建應用的哲學思路。這兩種思路,用術語來說就是jsp模式1和模式2,本質上的區別在于大部分請求的處理發生的位置。在模式1架構中,如圖1所示,jsp頁面獨立地負責處理請求和發送反饋給客戶端。這里仍然有內容和表現的分離,因為所有的數據訪問是使用bean完成的。盡管模式1架構應該很適合簡單應用,但是對于復雜的實現是不可取的。這種結構的任意使用通常會導致大量的腳本和java代碼嵌入到jsp頁面中,特別是在有大量的請求需要處理的情況下。盡管這可能對java開發者來說不是一個大問題,但是卻無疑是一個問題,如果你的jsp頁面是由設計師創建和維護的話——在大項目中通常如此。最終,這個問題甚至會導致角色定義和責任分配的混亂,引起本可以輕松避免的項目管理的麻煩。


圖1:jsp模式1結構

  模式2架構如圖2所示,是一個為動態內容服務的混合方案,因為它同時使用了servlet和jsp。它利用了兩種技術的優勢,使用jsp產生表現層而servlet負責執行敏感任務。在這里,servlet扮演控制器的角色,負責請求處理和產生jsp要使用的bean和對象,以及根據客戶的動作決定下一步轉發到哪一個jsp頁面。特別要注意的是jsp頁面內部并沒有處理邏輯;它只是簡單地負責取得可能是servelet事先創建的對象和bean,并為在了靜態模版中插入從servlet釋放出動態內容。我的觀點是,這個辦法一般會形成最干凈徹底的表現與內容的分離,使得你的開發團隊里的開發者和頁面設計師的角色與責任能夠清晰。實際上,你的應用越復雜,使用模式2帶來的好處就越多。


圖2:jsp模式2結構

  為了弄清模式2背后的概念,我們來參觀一個細化的具體實現:一個叫做音樂無界的在線音樂商店樣品。

  了解音樂無界

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

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


圖3:音樂無界,主視圖

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

<%
  for (int index=0; index < buylist.size(); index++) {
    cd anorder = (cd) buylist.elementat(index);
%>
  一旦描述物品的變量已創建,它們就簡單地被jsp表達式插入到靜態html模版中去。圖4顯示了用戶已經放了一些東西到購物車里去是的情況。


圖4:音樂無界,購物車視圖

  這里要注意的重要的一件事是對所有動作的處理既不發生在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中添加一件物品,請求都被發送到這個控制器servlet。它依次決定合適的動作,然后處理相應要添加的物品的請求參數。它就實例化一個新的cd bean(見清單4)代表這個選擇的物品,接著在把這個bean放進session之前處理購物車的更新。

清單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中還包括了額外的智能,因此它能夠知道如果選擇了一張已在購物車中的cd,那么應該簡單地增加session中cd bean的計數。它也處理從cart.jsp中觸發的動作,比如用戶從購物車中刪除物品,或是繼續去收銀臺結帳。注意控制器總是對哪個資源應該被調用來對特定的動作產生回饋有完全的控制權。例如,對購物車狀態的改變,像增加和刪除,會引起控制器將請求處理后轉發給eshop.jsp頁面。這樣引起該頁面依照已更新的購物車依次重新顯示主視圖。如果用戶決定結帳,則請求被處理后轉發給checkout.jsp(見清單5),通過后面的請求分配器,象下面顯示的這樣:

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中提出購物車并為此請求提取出總金額,然后顯示選中的物品和他們的總價格。圖5顯示了結算時的用戶視圖。一旦用戶去結帳,刪除session對象是同樣重要的。這個由頁面末端的session.invalidate()調用來完成。有兩個理由必須這樣做。第一,如果沒有使session無效,用戶的購物車不會重新初始化;如果用戶結帳后試圖開始新一輪的采購,她的購物車會繼續保存著已經付過錢的物品。第二,如果用戶結帳后僅僅是離開了網站,這個session對象不會被垃圾收集機制回收而是繼續占用寶貴的系統資源直到租約到期。因為缺省的session租約時間是大約三十分鐘,在一個大容量系統上這將會很快導致系統內存耗盡。當然,我們都知道對一個耗盡了系統內存的應用程序會發生什么。


圖5:結賬視圖

  注意這個應用所有的資源都是session相關的,因為這里的模式存儲在session里。因此,你必須確保用戶不會因為某些原因甚至由于錯誤直接訪問控制器。你可以在控制器檢測到缺少有效session的時候讓客戶端自動轉向到一個錯誤頁面(見列表6),來避免這種情況的發生。

列表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>

  部署音樂無界

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

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
只要你的服務器啟動,你應該可以使用url http://localhost:8080/examples/jsp/shopping/eshop.jsp來訪問這個應用程序。

  利用jsp和servlet

  著這個例子里,我們從細節上檢查了控制的層次和模式2架構提供的靈活性。實際上,我們已經看到了servlet和jsp頁面最好的特性是如何開發出最大化的內容與表現的剝離。只要正確地應用,模式2架構會將所有的處理邏輯集中到控制器servlet手里,而jsp頁面只負責視圖或者說表現層的工作。然而,使用模式2結構的阻力在于它的復雜性。因此,對于簡單應用使用模式1也是可以接受的。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲色图激情小说| 狠狠综合久久av一区二区小说| 国产精品嫩草影院久久久| 日韩高清中文字幕| 久久香蕉国产线看观看av| 亚洲精品国产综合区久久久久久久| 亚洲第一区第一页| 深夜福利一区二区| 久久精品91久久久久久再现| 欧美电影电视剧在线观看| 日韩视频欧美视频| 日韩动漫免费观看电视剧高清| 久久久极品av| 国产精品高清在线观看| 欧美色道久久88综合亚洲精品| 美女精品久久久| 亚洲第一区第二区| 日本aⅴ大伊香蕉精品视频| 久久久久久久一区二区三区| 91精品久久久久久久| 日韩精品日韩在线观看| 91精品国产91久久久久久吃药| 久久久亚洲成人| 成人国产精品一区| 国产精品一区二区三区久久| 2019中文字幕全在线观看| 国产精品视频一| 日本成人黄色片| 国产欧美精品日韩| 欧美在线免费视频| 日韩有码在线电影| 欧亚精品中文字幕| 日韩视频免费观看| 国产精品福利小视频| 亚洲女性裸体视频| 精品久久久国产精品999| 黄色一区二区三区| 成人天堂噜噜噜| 97香蕉久久夜色精品国产| 2024亚洲男人天堂| 色香阁99久久精品久久久| 综合av色偷偷网| 欧美激情视频播放| 国产精品久久久久久av下载红粉| 97人人爽人人喊人人模波多| 久久av资源网站| 国产欧美日韩免费| 亚洲国产97在线精品一区| 国产精品亚洲综合天堂夜夜| 欧洲亚洲妇女av| 国产精品成人一区二区三区吃奶| 欧美视频在线视频| 中文字幕精品www乱入免费视频| 国产精品免费视频久久久| 国产亚洲精品va在线观看| 98精品国产自产在线观看| 91在线直播亚洲| 国产女人18毛片水18精品| 尤物99国产成人精品视频| 亚洲欧美日韩一区二区三区在线| 日韩精品中文字幕在线观看| 午夜美女久久久久爽久久| 国产精品女视频| 国产精品久久久久免费a∨| 亚洲全黄一级网站| 日本欧美黄网站| 一区二区三区天堂av| 91精品啪在线观看麻豆免费| 57pao国产精品一区| 亚洲国产欧美久久| 久久久久www| 亚洲福利视频专区| 亚洲国内精品在线| 97国产在线观看| 欧美肥臀大乳一区二区免费视频| 成人免费福利在线| 亚洲国产精品va| 国产日韩欧美中文| 亚洲精品v欧美精品v日韩精品| 国产精品女人久久久久久| 国产91对白在线播放| 日本久久亚洲电影| 大伊人狠狠躁夜夜躁av一区| 日韩成人在线网站| 另类色图亚洲色图| 日韩av手机在线看| 国产成人精品日本亚洲| 色香阁99久久精品久久久| 亚洲国产精彩中文乱码av| 日本午夜精品理论片a级appf发布| 亚洲精品黄网在线观看| 日韩美女免费观看| xvideos亚洲| www.xxxx欧美| 亚洲欧美日韩精品久久亚洲区| 视频直播国产精品| 久久精品99久久久香蕉| 成人在线精品视频| 欧美一区第一页| 最近2019中文字幕第三页视频| 亚洲最大的网站| 全亚洲最色的网站在线观看| 欧美日韩另类在线| 亚洲精品成人免费| 国内外成人免费激情在线视频| 91在线国产电影| 青草热久免费精品视频| 欧美日韩在线视频观看| 国产欧美日韩精品在线观看| 一区二区福利视频| 国产精品久久中文| 色老头一区二区三区| 亚洲天堂网站在线观看视频| 在线播放亚洲激情| 91香蕉国产在线观看| 一区二区三区视频免费| 国产成人jvid在线播放| 国产午夜精品全部视频在线播放| 成人有码在线视频| 尤物yw午夜国产精品视频| 亚洲人成电影网站色| 国产日韩在线一区| 亚洲大胆美女视频| 亚洲国产另类 国产精品国产免费| 91精品国产乱码久久久久久蜜臀| 久久好看免费视频| 久久精品视频导航| 久久视频免费在线播放| 精品自拍视频在线观看| 日韩乱码在线视频| 久久久久免费精品国产| 揄拍成人国产精品视频| 欧美成人第一页| 久久精品欧美视频| 日韩在线免费视频观看| 日韩欧美综合在线视频| 狠狠躁夜夜躁人人爽天天天天97| 久久91亚洲精品中文字幕| 日韩精品视频在线免费观看| 日韩电影中文字幕av| 青青草国产精品一区二区| 精品一区二区三区四区在线| 91干在线观看| 亚洲欧美日韩成人| 日本精品一区二区三区在线播放视频| 日韩亚洲欧美中文在线| 69视频在线免费观看| 久久精品成人欧美大片古装| 国产拍精品一二三| 日本三级韩国三级久久| 亚洲精品国产精品国产自| 91香蕉嫩草神马影院在线观看| 日韩av高清不卡| 亚洲欧美视频在线| 亚洲字幕在线观看| 91精品国产成人| 国产精品成人v| 日韩美女视频免费在线观看| 久久6精品影院| 国产精品久久久久久亚洲影视| 久久久久久久久综合| 久久精品成人一区二区三区| 日韩欧美在线字幕| 欧美极品少妇全裸体|