天天看點

JSP 七講

<b>教</b><b>   </b><b>學</b><b>   </b><b>活</b><b>   </b><b>動</b><b>   </b><b>首</b><b>   </b><b>頁</b><b> </b>

<b>               </b>

<b>基</b><b>    </b><b>本</b><b>    </b><b>内</b><b>    </b><b>容</b><b> </b>

第 7 章 java servlet<b> </b>

<b>教學目的與要求:</b>通過本章的學習讓學生了解如何用 servlet 讀寫檔案,用 servlet 通路資料庫;了解servlet 工作原理,servlet共享變量的使用;掌握編譯和安裝 servlet,通過 jsp 頁面調用 servlet,httpservlet 類,掌握會話管理。

<b>教學内容:</b>

7.1 servlet 工作原理

7.2 編譯和安裝 servlet

7.3 通過 jsp 頁面調用 servlet

7.4 servlet共享變量

7.5 httpservlet 類

7.6 用 servlet 讀寫檔案

7.7 用 servlet 通路資料庫

7.8 會話管理<b> </b>

<b>教學基本要求:</b>

了解:用 servlet 讀寫檔案,用 servlet 通路資料庫

了解:servlet 工作原理,servlet共享變量

掌握:編譯和安裝 servlet,通過 jsp 頁面調用 servlet,httpservlet 類,會話管理<b> </b>

<b>教學重點教學難點:</b>

servlet 工作原理,編譯和安裝 servlet,通過 jsp 頁面調用 servlet,httpservlet 類,會話管理

<b>教學方法:</b>

<b>教學手段:</b>多媒體教學和計算機程式示範<b> </b>

<b>教學小結:</b><b> </b><b>(見教學程序)</b>

<b>作業與思考:</b>見課後習題<b> </b>

<b>課後記載:</b>

<b> </b>

<b></b>

<b>教</b><b>  </b><b>學</b><b>  </b><b>進</b><b>  </b><b>程</b><b> </b>

<b>第7章  java  servlet</b>

我們已經知道,sun公司以java servlet為基礎,推出了java server page。jsp提供了java servlet的幾乎所有好處,當一個客戶請求一個jsp頁面時,jsp引擎根據jsp頁面生成一個java檔案,即一個servlet。這一章,将對servlet做一個較詳細的介紹,這不僅對于深刻了解jsp有一定的幫助,而且通過學習servlet,還能使我們選擇使用jsp+javabeans+servlet的模式來開發我們的web應用程式。

我們已經知道,用jsp支援javabeans這一特點,可以有效的管理頁面的靜态部分和頁面的動态部分。另外,我們也可以在一個jsp頁面中調用一個servlet完成動态資料的處理,而讓jsp頁面本身處理靜态的資訊。是以,開發一個web應用有兩種模式可以選擇:

(1)  jsp+javabeans

(2)  jsp+javabeans+servlet

<b>7.1 servlet</b><b>工作原理</b>

servlet由支援servlet的伺服器:servlet引擎,負責管理運作。當多個客戶請求一個servlet時,引擎為每個客戶啟動一個線程而不是啟動一個程序,這些線程由servlet引擎伺服器來管理,與傳統的cgi為每個客戶啟動一個程序相比較,效率要高的多。

<b>7.1.1</b><b> servlet </b><b>的生命周期</b>

學習過java 語言的人對java applet(java小應用程式)都很熟悉,一個java applet是java.applet.applet類的子類,該子類的對象由用戶端的浏覽器負責初始化和運作。servlet的運作機制和applet類似,隻不過它運作在伺服器端。一個servlet是javax.servlet包中httpservlet類的子類,由支援servlet的伺服器完成該子類的對象,即servlet的初始化。

servlet的生命周期主要有下列三個過程組成:

(1)初始化servlet。servlet第一次被請求加載時,伺服器初始化這個servlet,即建立一個servlet對象,這對象調用init方法完成必要的初始化工作。

(2)誕生的servlet對象再調用service方法響應客戶的請求。

(3)當伺服器關閉時,調用destroy方法,消滅servlet對象。

init方法隻被調用一次,即在servlet第一次被請求加載時調用該方法。當後續的客戶請求servlet服務時,web服務将啟動一個新的線程,在該線程中,servlet調用service方法響應客戶的請求,也就是說,每個客戶的每次請求都導緻service方法被調用執行。

<b>7.1.2 </b><b>init</b><b>方法</b>

該方法是httpservlet類中的方法,我們可以在servlet中重寫這個方法。

方法描述:

public void init(servletconfig  config)  throws servletexception

servlet第一次被請求加載時,伺服器初始化一個servlet,即建立一個servlet對象,這個對象調用init方法完成必要的初始化工作。該方法在執行時,servlet引擎會把一個sevletconfig類型的對象傳遞給init()方法,這個對象就被儲存在servlet對象中,直到servlet對象被消滅,這個servletconfig對象負責向servlet傳遞服務設定資訊,如果傳遞失敗就會發生serveletexception,servlet就不能正常工作。

我們已經知道,當多個客戶請求一個servlet時,引擎為每個客戶啟動一個線程,那麼servlet類的成員變量被所有的線程共享。

<b>7.1.3</b><b> service</b><b>方法</b><b> </b>

該方法是httpservlet類中的方法,我們可以在servlet中直接繼承該方法或重寫這個方法。

public void service(httpservletrequest request  httpservletresponse  response)  throw

                   servletexception,ioexception

當servlet成功建立和初始化之後,servlet就調用service方法來處理使用者的請求并傳回響應。servlet引擎将兩個參數傳遞給該方法,一個httpservletrequest類型的對象,該對象封裝了使用者的請求資訊,此對象調用相應的方法可以擷取封裝的資訊,即使用這個對象可以擷取使用者送出的資訊。另外一個參數對象是httpservletresponse類型的對象,該對象用來響應使用者的請求。和init方法不同的是,init方法隻被調用一次,而service方法可能被多次的調用,我們已經知道,當後續的客戶請求servlet服務時,servlet引擎将啟動一個新的線程,在該線程中,servlet調用service方法響應客戶的請求,也就是說,每個客戶的每次請求都導緻service方法被調用執行,調用過程運作在不同的線程中,互不幹擾。

<b>7.1.4</b><b> destroy</b><b>方法</b>

該方法是httpservlet類中的方法。servlet可直接繼承這個方法,一般不需要重寫。

public destroy()

當servlet引擎終止服務時,比如關閉伺服器等,destroy()方法會被執行,消滅servlet對象。

<b>7.2 </b><b>編譯和安裝servlet</b>

<b>7.2.1</b><b> </b><b>簡單的servlet例子</b>

在下面的例子1中,hello擴充了httpservlet。

<b>例子1</b>

servlet源檔案

hello.java:

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class hello extends httpservlet

{  public void init(servletconfig config) throws servletexception

    {super.init(config);

    }

  public void service(httpservletrequest reqest,httpservletresponse response) throws ioexception

    { //獲得一個向客戶發送資料的輸出流:

       printwriter out=response.getwriter();

       response.setcontenttype("text/html;charset=gb2312");//設定響應的mime類型。

       out.println("&lt;html&gt; &lt;body&gt;");

       out.println("simple servlet");

       out.println("&lt;/body&gt; &lt;/html&gt;");

}

<b>7.2.2</b><b> </b><b>編譯servlet</b>

為了編譯servlet源檔案,需要httpservlet、httpservletrequest等類,jdk内置包中并不包含這些類檔案。為了能編譯servlet源檔案得到建立servlet用的位元組碼檔案,需要在環境變量中包含<b>servlet.jar</b>,這個jar檔案在tomcat安裝目錄的common\lib檔案下,如圖7.1所示(也可以到sun公司網站下載下傳編譯servlet所需要的類)。

對于window2000,用滑鼠右鍵點選“我的電腦”,彈出菜單,然後選擇屬性,彈出“系統特性”對話筐,再單擊該對話框中的進階選項,然後點選按鈕“環境變量”,編輯classpath,添加新的環境變量的值:

d:\tomcat\jakarta-tomcat-4.0\common\lib\servlet.jar;

我們将上述servlet的源檔案hello.java儲存到f:\2000,然後編譯生成位元組碼檔案hello.class

<b>7.2.3 </b><b>存放servlet的目錄</b>

(1)所有web服務目錄可使用的servlet的存放位置

如果讓所有web服務目錄都可以使用該servlet,那麼建立這個servlet的位元組碼檔案需存放在tomcat安裝目錄的classes目錄中,例如,本書所用機器的目錄就是:d:tomcat\jakarta-tomcat-4.0\classes,如圖7.1所示。

我們已經知道,servlet第一次被請求加載時,伺服器初始化一個servlet,即建立一個servlet對象,這對象調用init方法完成必要的初始化工作。如果你對servlet的源檔案進行了修改,并将新的位元組碼檔案存放到classes中,如果伺服器沒有關閉的話,新的servlet不會被建立,因為,當後續客戶請求servlet服務時,已初始化的servlet将調用service方法響應客戶。

(2)隻對examples服務目錄可用的seclet的存放目錄

examples是tomcat引擎的預設web服務目錄之一。

如果想讓某個servlet隻對examples目錄可用,那麼建立該servlet的位元組碼檔案隻需存放在webapps/example/web-inf/classes目錄中。

存放在該目錄中的servlet和存放在上面(1)中所述目錄中的servlet有所不同,伺服器引擎首先檢查webapps/example/web-inf/classes目錄中的建立該servlet的位元組碼檔案是否被修改過,如果重新修改過,就會用消滅servlet,用新的位元組碼重新初始化servlet。

如果經常調試servlet,可以把servlet放在webapps/example/web-inf/classes。需要注意的是,當使用者請求servlet服務時,由于伺服器引擎每次都要檢查位元組碼檔案是否被修改過,導緻伺服器的運作效率降低。

<b>7.2.4 </b><b>運作servlet</b>

如果一個servlet對所有的web服務目錄可用,那麼隻要在伺服器引擎啟動後,在浏覽器位址欄鍵入:

http://localhost:8080/web服務目錄/servlet/建立servlet類的名字

即可,例如,對于用上述hello建立的servlet,

(1) root 服務目錄

http://localhost:8080/servlet/hello

(2) friend目錄(我們自定義的一個web服務目錄)

http://localhost:8080/friend/servlet/hello

如果是隻對examples服務目錄可用的servelt,那麼隻要在伺服器引擎啟動後,在浏覽器位址欄鍵入:

http://localhost:8080/examples/servlet/建立servlet類的名字

我們将hello.class檔案儲存到tomcat引擎的classes檔案夾中。圖7.2和7.3是在不同的web目錄下運作servlet的效果。

<b>7.2.5 </b><b>帶包名的servle</b><b>t</b>

在寫一個servlet的java檔案時,可以使用package語句給servlet一個包名。包名可以是一個合法的辨別符,也可以是若幹個辨別符加“.”分割而成,如:

package gping;

package tom.jiafei;

程式如果使用了包語句,例如

那麼在classes目錄下需有如下的子目錄,例如,在d:\tomcat\jakarta-tomcat-4.0\classes下再建立如下的目錄結構。

\tom\jiafei

并将servlet的位元組碼檔案存在該目錄中,如圖7.4所示。

如果servlet有包名,比如,hello的包名是tom.jiafei,那麼調用該servlet的url是:

http://localhost:8080/web服務目錄/servlet/tom.jiafei.hello

因為起了包名,hello的全名是tom.jiafei.hello(就好比大連的全名是:中國.遼甯.大連)。

<b>7.3 </b><b>通過jsp頁面調用servlet</b>

<b>7.3.1</b><b> </b><b>通過表單向servlet送出資料</b>

任何一個web服務目錄下的jsp頁面都可以通過表單或超連結通路某個servlet。通過jsp頁面通路servlet的好處是,jsp頁面可以負責頁面的靜态資訊處理,動态資訊處理交給servlet去完成。

在下面的例子中,jsp頁面通過表單向servlet送出一個正實數,servlet負責計算這個數的平方根傳回給客戶。

為了友善地調試servlet,本書中,servlet的位元組碼檔案存放在d:\tomcat\jakarta-tomcat-4.0\webapps\example\web-inf\classes中,那麼在jsp頁面中調用servlet時,servlet的url是:

/examples/servlet/servletname

在下面的例子2中,jsp頁面通過表單送出一個正數,servlet負責計算這個數的平方根。

<b>例子2</b>

調用servlet的頁面(該頁面存放在web服務的根目錄root中)

givenumber.jsp(效果如圖7.5所示)

&lt;%@ page contenttype="text/html;charset=gb2312" %&gt;

&lt;html&gt;

&lt;body bgcolor=cyan&gt;&lt;font size=1&gt;

&lt;p&gt;輸入一個數,servlet求這個數的平方根:

&lt;form action="examples/servlet/sqrt" method=get&gt;

  &lt;input type=text name=number&gt;

  &lt;input type=submit value="送出"&gt;

&lt;/form&gt;

&lt;/body&gt;

&lt;/html&gt;

servlet源檔案(效果如圖7.6所示)

sqrt.java

public class sqrt extends httpservlet

{   public void init(servletconfig config) throws servletexception

  public void service(httpservletrequest request,httpservletresponse response) throws ioexception

       string number=request.getparameter("number");   //擷取客戶送出的資訊。

       double n=0;

        try{ n=double.parsedouble(number);

             out.print("&lt;br&gt;"+math.sqrt(n));

           }

        catch(numberformatexception e)

           { out.print("&lt;h1&gt;input number letter please! &lt;/h1&gt;");

           }      

<b>7.3.2 </b><b>通過超連結通路servlet</b>

我們可以在jsp頁面中,點選一個超連結,通路servlet。

例子3

connection.jsp:

&lt;a href="/servlet/hello" &gt;加載servlet&lt;a&gt;

<b>7.4 servlet</b><b>的共享變量</b>

我們已經知道,在servlet被加載之後,當後續的客戶請求servlet服務時,引擎将啟動一個新的線程,在該線程中,servlet調用service方法響應客戶的請求,而且servlet類中定義的成員變量,被所有的客戶線程共享。在下面的例子4中,利用共享變量實作了一個計數器。

<b>例子4</b>(效果如圖7.7所示)

count.java:

public class count extends httpservlet

{ int count;

  public void init(servletconfig config) throws servletexception

     count=0;

  public synchronized void service(httpservletrequest request,httpservletresponse response)

                             throws ioexception

       response.setcontenttype("text/html;charset=gb2312");//設定響應的mime類型。

        count++;                         //增加計數。

       out.println("you are "+count+"th"+" people");

注:在處理多線程問題時,我們必須注意這樣一個問題:當兩個或多個線程同時通路同一個變量,并且一個線程需要修改這個變量。我們應對這樣的問題作出處理,否則可能發生混亂.是以上述例子中的service方法是一個synchronized方法。

數學上有一個計算π的公式:

π/4=1-1/3+1/5-1/7+1/9-1/11…  …

下面的例子中利用成員變量被所有客戶共享這一特性實作客戶幫助計算 的值,即每當客戶請求通路servlet時都參與了一次的計算。

客戶通過點選一個jsp頁面的超連結通路一個計算 的servlet

<b>例子5</b>(效果如圖7.8所示)

jsp頁面

example7_5.jsp:

&lt;body bgcolor=cyan&gt;

&lt;a href="examples/servlet/computerpi" &gt;檢視pi的值&lt;a&gt;

public class computerpi extends httpservlet

{ double sum=0,i=1,j=1;

  int number=0;

  public synchronized void  service(httpservletrequest request,httpservletresponse response)

                        throws ioexception

       response.setcontenttype("text/plain");//設定響應的mime類型為純文字。

         number++;

         sum=sum+i/j;

         j=j+2;

         i=-i; 

       out.println("you are "+number+"th people coming to here");

       out.println("now pi= "+4*sum);

<b>7.5 httpservlet </b><b>類</b>

<b>7.5.1</b><b> doget</b><b>方法和dopost方法</b>

httpservlet除了init、service、destroy方法外,該類還有兩個很重要的方法:doget和dopost,用來處理客戶的請求并作出響應。

當伺服器引擎第一次接受到一個servlet請求時,會使用init方法初始化一個servlet,以後每當伺服器再接受到一個servlet請求時,就會産生一個新線程,并在這個線程中調用service方法檢查http請求類型(get 、post等),并在service方法中根據使用者的請求方式,對應地再調用doget或dopost方法。是以,在servlet類中,我們不必重寫service方法來響應客戶,直接繼承service方法即可。我們可以在servlet類中重寫dopost或doget方法來響應使用者的請求,這樣可以增加響應的靈活性,并降低伺服器的負擔。

如果不論使用者請求類型是post還是get,伺服器的處理過程完全相同,那麼我們可以隻在dopost方法中編寫處理過程,而在doget方法中再調用dopost方法即可,或隻在doget方法中編寫處理過程,而在dopost方法中再調用doget方法(見例子6)。

如果根據請求的類型進行不同的處理,就需在兩個方法中編寫不同的處理過程(見例子7)。

在下面的例子6中,使用者可以通過兩個表單向servlet送出一個正數,其中一個表單的送出方式是post,另一個表單的方式是get。無論使用者用那種方式,伺服器的servlet都計算這個數的全部因數,傳回給使用者。而在下面的例子7中,如果使用post方式送出正數,servlet計算這個數的全部因數,如果使用get方式,servlet求出小于這個數的全部素數。

例子6(效果如圖7.9所示)

送出正數的jsp頁面

example7_6.jsp:

&lt;p&gt;輸入一個數,送出給servlet(post方式):

&lt;form action="examples/servlet/computerfactor" method=post&gt;

&lt;p&gt;輸入一個數,送出給servlet(get方式):

&lt;form action="examples/servlet/computerfactor" method=get&gt;

sevlet源檔案

computerfacor.java:

public class computerfactor extends httpservlet

  public  void  dopost(httpservletrequest request,httpservletresponse response)

                        throws servletexception,ioexception

    {  //獲得一個向客戶發送資料的輸出流:

       out.println("&lt;html&gt;");

       out.println("&lt;body&gt;");

       string number=request.getparameter("number");  //擷取客戶送出的資訊。

             out.println("&lt;h1&gt; factors of "+n+" :&lt;/h1&gt;");

             //求n的全部因數:

             for(int i=1;i&lt;=n;i++)

                 { if(n%i==0)

                   out.println(i);

                 }

  public  void  doget(httpservletrequest request,httpservletresponse response)

    {

       dopost(request,response);

<b>例子7</b>(效果如圖7.10所示)

example7_7.jsp:

&lt;form action="examples/servlet/computerfactorandprimnumber" method=post&gt;

&lt;form action="examples/servlet/computerfactorandprimnumber" method=get&gt;

computerfacorandprimnumber.java:

public class computerfactorandprimnumber  extends httpservlet

                        throws servletexception,ioexception

    {  printwriter out=response.getwriter();

             out.println("&lt;h1&gt; primnumbers less "+n+" :&lt;/h1&gt;");

             //求小于n的全部素數:

             int j=1;

             for(int i=1;i&lt;n;i++)

                 { for(j=2;j&lt;i;j++)

                    {if(i%j==0)

                        break;

                    }

                   if(j&gt;=i)

                    { out.println(i);

<b>7.5.2 </b><b>處理http請求頭及表單資訊</b>

有關http請求頭的和表單的介紹,可參見第3章。

在下面的例子8中,servlet顯示請求的http頭的值和表單送出的資訊(可參考對比第3章例子4)。

<b>例子8</b>

送出資訊的jsp頁面

example7_8.jsp:

   &lt;form action="examples/servlet/getmessages" method=post name=form&gt;

       &lt;input type="text" name="boy"&gt;

       &lt;input type="submit" value="enter" name="submit"&gt;

   &lt;/form&gt;

&lt;/font&gt;

處理http請求頭的sevlet源檔案

getmessages.java:

import java.util.*;

public class getmessages  extends httpservlet

       // 客戶使用的協定是:

        out.println("&lt;br&gt;protocol:");

        string protocol=request.getprotocol();

        out.println(protocol);

        //擷取接受客戶送出資訊的servlet:

        out.println("&lt;br&gt;accept servlet:");

        string path=request.getservletpath();

        out.println(path);

        //客戶送出的資訊的長度:

        out.println("&lt;br&gt;message length:");

        int length=request.getcontentlength();

        out.println(length);

       // 客戶送出資訊的方式:

        out.print("&lt;br&gt; method:");

        string method=request.getmethod();

        out.println(method);

        //擷取http頭檔案中user-agent的值:

        out.println("&lt;br&gt; user-agent:");

        string header1=request.getheader("user-agent");

        out.println(header1);

       //擷取http頭檔案中accept的值:

         out.println("&lt;br&gt; accept:");

        string header2=request.getheader("accept");

        out.println(header2);

       // 擷取http頭檔案中host的值:

         out.println("&lt;br&gt; host:");

        string header3=request.getheader("host");

        out.println(header3);

       //擷取http頭檔案中accept-encoding的值:

        out.println("&lt;br&gt; accept-encoding:");

        string header4=request.getheader("accept-encoding");

        out.println(header4);

      //擷取客戶的ip位址:

        out.println("&lt;br&gt; client ip:");

        string  ip=request.getremoteaddr();

        out.println(ip);

      // 擷取客戶機的名稱:

        out.println("&lt;br&gt; client name:");

        string clientname=request.getremotehost();

        out.println(clientname);

      // 擷取伺服器的名稱:

        out.println("&lt;br&gt; server name:");

        string servername=request.getservername();

        out.println(servername);

      // 擷取伺服器的端口号:

        out.println("&lt;br&gt; serverport:");

        int serverport=request.getserverport();

        out.println(serverport);

       //擷取用戶端送出的所有參數的名字:

        out.println("&lt;br&gt;parameter names");

        enumeration enum=request.getparameternames();

             while(enum.hasmoreelements())

              {string s=(string)enum.nextelement();

                out.println(s);

              }     

     //  文本框text送出的資訊:

          out.println("&lt;br&gt; text:");

          string str=request.getparameter("boy");

                 out.println(str);

       out.println("&lt;/body&gt;");

       out.println("&lt;/html&gt;");

      dopost(request,response);

下面的例子9用servlet實作使用者注冊。使用者通過一個jsp頁面送出姓名和email位址實作注冊。當servlet擷取這些資訊後,首先檢查散清單對象中是否已經存在這個名字,該散清單存儲了已經注冊的使用者的名字。如果目前準備注冊的使用者送出的名字在散清單中已經存在,就提示客戶更換名字,否則将檢查客戶是否提供了書寫正确的email位址,如果提供了書寫正确email位址将允許注冊(僅僅要求email位址中不允許出現空格)。

<b>例子9</b>(效果如圖7.11所示)

送出注冊名字的jsp頁面

example7_9.jsp:

&lt;body bgcolor=cyan&gt;&lt;font size=1 &gt;  

  &lt;form action="examples/servlet/loginbyservlet" method=post &gt;

     &lt;p&gt;輸入你的姓名:

     &lt;input type="text" name="name" value="abc"&gt;

     &lt;br&gt;

     &lt;p&gt;輸入你的e-mail位址:

      &lt;input type="text" name="address" value="[email protected]"&gt;

     &lt;p&gt;點選送出按鈕:

     &lt;br&gt; 

     &lt;input type="submit" value="送出" name=submit&gt;

  &lt;/form&gt;

&lt;/font&gt; 

loginbyservlet.java:

public class loginbyservlet  extends httpservlet

{  hashtable hashtable=new hashtable();

  public synchronized void dopost(httpservletrequest request,httpservletresponse response)

       response.setcontenttype("text/html;charset=gb2312");outputstream();

       response.setcontenttype("text/html");//設定響應的mime類型。

       //擷取使用者送出的名字:

       string person_name=request.getparameter("name"),

              name_found=null;

         if(person_name==null)

             {person_name="";

             }

        //在散清單查找是否已存在該名字:

        name_found=(string)hashtable.get(person_name);

       if(name_found==null)

          { string person_email=request.getparameter("address");

              if(person_email==null)

                  {person_email="";

                  }

            stringtokenizer fenxi=new stringtokenizer(person_email,"  @");

            int n=fenxi.counttokens();

             if(n&gt;=3)

                 {out.print("&lt;br&gt;"+"there are exists illegal letters in your email");

             else

                { hashtable.put(person_name,person_name);

                  out.print("&lt;br&gt;"+"login success!");

                  out.print("&lt;br&gt;"+"your name is "+person_name);

                }

          }

        else

          {out.print("&lt;br&gt;"+"this name has exist ");

  public synchronized void  doget(httpservletrequest request,httpservletresponse response)

<b>7.5.3 </b><b>設定響應的http頭</b>

有關響應的http頭介紹,可參見第3章。

在下面的例子10中,servlet設定響應頭:refresh的頭值是2,那麼該servlet在2秒鐘後自動重新整理,即servlet在2秒鐘後重新調用service方法響應使用者。

<b>例子10</b>(效果如圖7.12所示)

調用servlet的jsp頁面

example7_10:

&lt;a href="examples/servlet/dateservlet" &gt;檢視時間&lt;a&gt;

dateservlet.java:

public class dateservlet  extends httpservlet

  public  void dopost(httpservletrequest request,httpservletresponse response)

       response.setheader("refresh","2");  //設定refresh的值。

       out.println("now time:");

       out.println("&lt;br&gt;"+new date());

下面例子11實作servlet的重定向,客戶通路servlet:day;如果通路的時間是在22點之後,就被重定向到servlet:night,提醒使用者休息。day和night被存放在examples/web-inf/classes中。

<b>例子11</b>(效果如圖7.13所示)

day.java:

public class day extends httpservlet

       calendar calendar=calendar.getinstance(); //建立一個月曆對象。

       calendar.settime(new date());//用目前時間初始化月曆時間。

       int hour=calendar.get(calendar.hour_of_day),

       minute=calendar.get(calendar.minute),

       second=calendar.get(calendar.second);

       if(hour&gt;=22)

           {response.sendredirect("night");   //重定向。

           { out.print("now time :");

             out.print(hour+":"+minute+":"+second);           

      out.println("&lt;/body&gt;");

      out.println("&lt;/html&gt;");

  public void doget(httpservletrequest request,httpservletresponse response)

night.java:

public class night extends httpservlet

       out.println("&lt;h1&gt; it is time to sleep");

<b>7.6 </b><b>用servlet讀寫檔案</b>

這節内容涉及到的檔案操作及輸入、輸出流的内容可參見第4章。

<b>7.6.1 </b><b>讀取檔案的内容</b>

在下面的例子12中,通過一個jsp頁面顯示給使用者一些jsp檔案的名字,該jsp檔案存放在root服務目錄下。使用者可以通過post或get方式将檔案的名字送出給一個servlet,該servelt存放在服務目錄examples下的web-inf/classes中。這個servlet将根據送出方式的不同,分别讀取jsp檔案的源代碼給客戶,或顯示該jsp檔案的運作效果給客戶。

<b>例子12</b>(效果如圖7.14、7.15、7.16所示)

送出檔案名字的jsp頁面

read.jsp:

&lt;%@ page import ="java.io.*" %&gt;

&lt;%! class filejsp implements filenamefilter

     { string str=null;

         filejsp(string s)

         {str="."+s;

         }

    public  boolean accept(file dir,string name)

         { return name.endswith(str);

         }             

     }

%&gt;

&lt;p&gt;下面列出了伺服器上的一些jsp檔案

 &lt;% file dir=new file("d:/tomcat/jakarta-tomcat-4.0/webapps/root/");

   filejsp file_jsp=new filejsp("jsp");

   string file_name[]=dir.list(file_jsp);

    for(int i=0;i&lt;5;i++)

           {out.print("&lt;br&gt;"+file_name[i]);

 %&gt;

 &lt;br&gt;輸入檔案的名字讀取jsp檔案的源代碼内容:

  &lt;form action="examples/servlet/readfileservlet" method=post&gt;

     &lt;input type="text" name="name"&gt;

     &lt;input type=submit value="送出"&gt;

&lt;br&gt;輸入檔案的名字顯示該jsp檔案的運作效果:

  &lt;form action="examples/servlet/readfileservlet" method=get&gt;

讀取檔案的servlet源檔案

readfileservlet:

public class readfileservlet extends httpservlet

  //dopost方法使用了回壓流來讀取jsp檔案的源代碼:

    {  //擷取送出的檔案的名字:

       string name=request.getparameter("name");

        //獲得一個向客戶發送資料的輸出流:

       file f=new file("d:/tomcat/jakarta-tomcat-4.0/webapps/root",name);

       try{ filereader in=new filereader(f) ;

            pushbackreader push=new pushbackreader(in);

            int c;

            char b[]=new char[1];             

            while ( (c=push.read(b,0,1))!=-1)//讀取1個字元放入字元數組b。

             { string s=new string(b);

                if(s.equals("&lt;"))        //回壓的條件 

                {  push.unread('&amp;');

                   push.read(b,0,1); //push讀出被回壓的字元位元組,放入數組b.

                   out.print(new string(b));

                   push.unread('l');

                   push.unread('t');

            else if(s.equals("&gt;"))        //回壓的條件 

               {   push.unread('&amp;');

                   push.unread('g');

                   out.print(new string(b));

               }

            else if(s.equals("\n"))       

              { out.print("&lt;br&gt;");

              }

            else

               {out.print(new string(b));

          push.close();

        }

      catch(ioexception e){}    

  //doget方法将顯示jsp源檔案運作的效果

    {  string name=request.getparameter("name");

            bufferedreader bufferin=new bufferedreader(in);

            string str=null; 

            while((str=bufferin.readline())!=null)

               {out.print("&lt;br&gt;"+str);

            bufferin.close();

            in.close();

<b>7.6.2 </b><b>寫檔案</b>

在這節,我們通過一個servlet實作小說内容的續寫來說明servlet在寫檔案中的技巧。

在下面的例子13中,通過一個jsp頁面顯示給使用者一個小說檔案的已有内容,小說檔案存放在伺服器的f:/2000下,檔案名字是story.txt。jsp檔案存放在root服務目錄下。使用者可以通過post方式将小說的新内容送出給一個servlet,該servelt存放在服務目錄examples下的web-inf/classes中。這個servlet将使用者送出的内容寫入小說檔案的尾部。

<b>例子13</b>(效果如圖7.17所示)

送出小說内容的jsp頁面

story.jsp:

&lt;body&gt;

 &lt;h4&gt;小說已有内容:&lt;/h4&gt;

&lt;font size=1 color=blue&gt;

 &lt;% file f=new file("f:/2000","story.txt");

    //列出小說的内容:

        try{ randomaccessfile file=

             new randomaccessfile(f,"r");

             string temp=null;

             while((temp=file.readutf())!=null)

                {  byte  d[]=temp.getbytes("iso-8859-1");

                  temp=new string(d);

                  out.print("&lt;br&gt;"+temp);

             file.close();

        catch(ioexception e){}

    &lt;p&gt;請輸入續寫的新内容:

    &lt;form action="examples/servlet/write" method=post name=form&gt;

      &lt;textarea name="content" rows="12" cols=80 wrap="physical"&gt;

      &lt;/textarea&gt;

      &lt;br&gt;

      &lt;input type="submit" value="送出内容" name="submit"&gt;

     &lt;/form&gt;

續寫檔案的servlet源檔案:

write.java:

public class write extends httpservlet

{  //聲明一個共享的檔案和共享字元串:

    file f=null;

    string use="yes" ;

    {   //擷取送出的檔案内容:

       string content=request.getparameter("content");

       f=new file("f:/2000","story.txt");

      //把對檔案的操作放入一個同步塊中,并通知

      //其它使用者該檔案正在被操作中:

      if(use.startswith("yes"))

        { synchronized(f)

            {  use="using";

              try{

                  randomaccessfile file=new randomaccessfile(f,"rw");

                  file.seek(file.length()); //定位到檔案的末尾。

                  file.writeutf(content);

                  file.close();

                  use="yes";

                  out.print("&lt;br&gt;"+"contents have been write to file");

              catch(ioexception e){}

            }

       //如果該小說正在被續寫,就通知客戶等待:

      else

          {out.print("file is writing,wait please");

          } 

    { 

<b>7.7 </b><b>用servlet通路資料庫</b>

有關資料庫連接配接的一些知識可參見第5章。本節通過例子說明servlet在資料庫方面的應用。我們仍然使用第5章的資料源sun ,該資料源為server伺服器上的pubs資料庫,該庫有一個表:students。

<b>7.7.1 </b><b>資料庫記錄查詢</b>

在下面的例子14中,客戶通過condition.jsp頁面輸入查詢條件,例如,查詢某個姓名的成績、查詢成績在某個分數段範圍内的學生成績等等。使用者通過post方式送出姓名給servlet;分數區間通過get方式送出給servlet。該servlet根據不同的送出方式采取相應的查詢方法。

<b>例子14</b>(效果如圖7.18所示)

送出查詢條件的jsp頁面

condition.jsp:

&lt;font size=1&gt;

&lt;form action="examples/servlet/inquire" method="post"&gt;

 &lt;p&gt;成績查詢

 &lt;p&gt;輸入姓名:

  &lt;input type=text name="name"&gt;

  &lt;input type=submit name="g" value="送出"&gt;

&lt;form action="examples/servlet/inquire" method="get" &gt;

 &lt;p&gt;根據分數查詢名單:&lt;br&gt;

  英語分數在

 &lt;input type=text name="englishmin" value=1&gt;

  和

 &lt;input type=text name="englishmax" value=100&gt;

  之間

 &lt;br&gt; 數學分數在

 &lt;input type=text name="mathmin" value=1&gt;

 &lt;input type=text name="mathmax" value=100&gt;

  之間  &lt;br&gt;

  &lt;input type=submit  value="送出"&gt;

負責查詢的servlet源檔案:

inquire.java:

import java.sql.*;

public class inquire extends httpservlet

   //通過post方法按名字查詢記錄:

  { printwriter out=response.getwriter();

    response.setcontenttype("text/html;charset=gb2312");//設定響應的mime類型。

    out.println("&lt;html&gt;");

    out.println("&lt;body&gt;");

  //擷取送出的姓名:

    string name=request.getparameter("name");

    string number,xingming;

    connection con=null;

    statement sql=null;

    resultset rs=null;

    int math,english,physics;

       try{class.forname("sun.jdbc.odbc.jdbcodbcdriver");

       catch(classnotfoundexception e){}

       try

      {    con=drivermanager.getconnection("jdbc:odbc:sun","sa","");

           sql=con.createstatement();

           string condition="select * from students where 姓名 = "+"'"+name+"'";

           rs=sql.executequery(condition);

        out.print("&lt;table border&gt;");

            out.print("&lt;tr&gt;");

            out.print("&lt;th width=100&gt;"+"number");

            out.print("&lt;th width=100&gt;"+"name");

            out.print("&lt;th width=50&gt;"+"math");

            out.print("&lt;th width=50&gt;"+"english");

            out.print("&lt;th width=50&gt;"+"phsics");

            out.print("&lt;/tr&gt;");

       while(rs.next())

           { out.print("&lt;tr&gt;");

             number=rs.getstring(1);

             out.print("&lt;td &gt;"+number+"&lt;/td&gt;");

             xingming=rs.getstring(2);

             out.print("&lt;td &gt;"+xingming+"&lt;/td&gt;");

             math=rs.getint("數學成績");

             out.print("&lt;td &gt;"+math+"&lt;/td&gt;");

             english=rs.getint("英語成績");

             out.print("&lt;td &gt;"+english+"&lt;/td&gt;");

             physics=rs.getint("實體成績");

             out.print("&lt;td &gt;"+physics+"&lt;/td&gt;");  

            out.print("&lt;/tr&gt;") ;          

            }

       out.print("&lt;/table&gt;");

      con.close();

    catch(sqlexception e)

          {

  }

  //通過get方法按成績查詢記錄:

   //擷取送出的分數的最大值和最小值:

    string englishmax=request.getparameter("englishmax");

    string englishmin=request.getparameter("englishmin");

    string mathmax=request.getparameter("mathmax");

    string mathmin=request.getparameter("mathmin");

           string econdition="英語成績 &lt;= "+englishmax+" and "+"英語成績 &gt;= "+englishmin;

           string mcondition="數學成績 &lt;= "+mathmax+" and "+"數學成績 &gt;= "+mathmin;

           string condition="select * from students where "+mcondition+" and "+econdition;

       out.print("&lt;table border&gt;");

           { out.print("&lt;tr&gt;");

             out.print("&lt;td &gt;"+math+"&lt;/td&gt;");

            out.print("&lt;/tr&gt;") ;          

     {

    out.println("&lt;/body&gt;");

    out.println("&lt;/html&gt;");

<b>7.7.2 </b><b>使用共享連接配接</b>

資料庫操作中,建立連接配接是耗時最大的操作之一。如果客戶通路的是同一資料庫,那麼,為每個客戶都建立一個連接配接是不合理的。我們已經知道,servlet的成員變量是被所有使用者共享的。這樣,我們可以把connection對象作為一個成員變量被所有的客戶共享,也就是說第一個通路資料庫的客戶負責建立連接配接,以後所有的客戶共享這個連接配接,每個客戶都不要關閉這個共享的連接配接。下面的servlet使用共享連接配接查詢資料庫的所有記錄。

<b>例子15</b>

使用共享連接配接的servlet源檔案

shareinquire.java:

public class shareinquire extends httpservlet

{  connection con=null; //共享連接配接。

      //加載jdbc-odbc橋接器:

    if(con==null)

    { try

       {   //第一個使用者負責建立連接配接con。

           con=drivermanager.getconnection("jdbc:odbc:sun","sa","");

           string condition="select * from students";

           rs=sql.executequery(condition);

            out.print("&lt;/tr&gt;");

           { out.print("&lt;tr&gt;");

             out.print("&lt;td &gt;"+rs.getstring(1)+"&lt;/td&gt;");

             out.print("&lt;td &gt;"+rs.getstring(2)+"&lt;/td&gt;");

             out.print("&lt;td &gt;"+rs.getint("數學成績")+"&lt;/td&gt;");

             out.print("&lt;td &gt;"+rs.getint("英語成績")+"&lt;/td&gt;");

             out.print("&lt;td &gt;"+rs.getint("實體成績")+"&lt;/td&gt;");  

             out.print("&lt;/tr&gt;") ;         

         out.print("&lt;/table&gt;");

       }

     catch(sqlexception e)

          {

   //其它客戶通過同步塊使用這個連接配接:

   else

    { synchronized(con)

       {try{ sql=con.createstatement();

             string condition="select * from students";

             rs=sql.executequery(condition);

             out.print("&lt;table border&gt;");

             out.print("&lt;tr&gt;");

             out.print("&lt;th width=100&gt;"+"number");

             out.print("&lt;th width=100&gt;"+"name");

             out.print("&lt;th width=50&gt;"+"math");

             out.print("&lt;th width=50&gt;"+"english");

             out.print("&lt;th width=50&gt;"+"phsics");

             out.print("&lt;/tr&gt;");

             while(rs.next())

              { out.print("&lt;tr&gt;");

                out.print("&lt;td &gt;"+rs.getstring(1)+"&lt;/td&gt;");

                out.print("&lt;td &gt;"+rs.getstring(2)+"&lt;/td&gt;");

                out.print("&lt;td &gt;"+rs.getint("數學成績")+"&lt;/td&gt;");

                out.print("&lt;td &gt;"+rs.getint("英語成績")+"&lt;/td&gt;");

                out.print("&lt;td &gt;"+rs.getint("實體成績")+"&lt;/td&gt;");  

                out.print("&lt;/tr&gt;") ;         

            out.print("&lt;/table&gt;");

        catch(sqlexception e)

   out.println("&lt;/body&gt;");

   out.println("&lt;/html&gt;");

 }

                        throws servletexception,ioexception

 {  dopost(request,response);

<b>7.8</b><b>會話管理</b>

<b>7.8.1</b><b> </b><b>擷取使用者的會話</b>

我們已經知道,http協定是一種無狀态協定。一個客戶向伺服器送出請求(request)然後伺服器傳回響應(respons),連接配接就被關閉了。在伺服器端不保留連接配接的有關資訊,是以當下一次連接配接時,伺服器已沒有以前的連接配接資訊了,無法判斷這一次連接配接和以前的連接配接是否屬于同一客戶。是以,必須使用客戶的會話,記錄有關連接配接的資訊。

一個servlet使用httpservletrequest 對象request調用getsession方法擷取使用者的會話對象:

httpsession session=request.getsession(true);

一個使用者在不同的servlet中擷取的session對象是完全相同的,不同的使用者的session對象互不相同。有關會話對象常用方法可參見第4章。

在下面的例子16中,有兩個servlet,boy和girl。客戶通路boy時,将一個字元串對象,存入自己的會話中,然後通路girl,在girl中再輸出自己的session對象中的字元串對象。

例子16(效果如圖7.19所示)

boy.java:

public class boy extends httpservlet

       httpsession session=request.getsession(true);  //擷取客戶的會話對象

                   session.setattribute("name","zhoumin");

       out.println(session.getid());                  //擷取會話的id.

        out.println("&lt;/body&gt;");

    { dopost(request,response);

girl.java:

public class girl extends httpservlet

       httpsession session=request.getsession(true);  //擷取客戶的會話對象

       string s=(string)session.getattribute("name"); //擷取會話中存儲的資料。

       out.print("&lt;br&gt;"+s);

                        throws servletexception,ioexception

<b>7.8.2 </b><b>購物車</b>

使用者通過一個jsp頁面:choice.jsp選擇商品,送出給servlet:addcar,該servlet負責将商品添加到使用者的session對象中(相當于使用者的一個購物車),并将session對象中的商品顯示給使用者。使用者可以不斷地從choice.jsp頁面送出商品給addcar。使用者通過remove.jsp頁面選擇要從購物車中删除的商品送出給servlet:removegoods,該servlet負責從使用者的購物車(使用者的session對象)删除商品。

<b>例子17</b>(效果如圖7.20、7.21所示)

負責選擇商品的jsp頁面

choice.jsp:

&lt;%@ page import="java.util.*" %&gt;

&lt;%@ page import="car1" %&gt;

&lt;p&gt;這裡是第一百貨商場,選擇您要購買的商品添加到購物車:

&lt;form action="examples/servlet/addcar" method=post name=form&gt;

       &lt;select name="item"  value="沒選擇"&gt;

          &lt;option value="tv"&gt;電視機

          &lt;option value="apple"&gt;蘋果

          &lt;option value="coke"&gt;可口可樂

          &lt;option value="milk"&gt;牛奶

          &lt;option value="tea"&gt;茶葉

       &lt;/select&gt;

&lt;p&gt;輸入購買的數量:

    &lt;input type=text name="mount"&gt;

&lt;p&gt;選擇計量機關:

   &lt;input type="radio" name="unit" value="個"&gt;個

   &lt;input type="radio" name="unit" value="公斤"&gt;公斤

   &lt;input type="radio" name="unit" value="台"&gt;台

   &lt;input type="radio" name="unit" value="瓶"&gt;瓶

&lt;input type=submit value="送出添加"&gt;

負責添加商品的servlet

addcar.java:

public class addcar extends httpservlet

   public void init(servletconfig config) throws servletexception

       string item =request.getparameter("item"),      //擷取客戶選擇的商品名稱。

              mount=request.getparameter("mount"),    //擷取客戶購買的數量。

              unit =request.getparameter("unit");     //擷取商品的計量機關。

       //将客戶的購買資訊存入客戶的session對象中。

        string str="name: "+item+"  mount:"+mount+"  unit:"+unit;

               session.setattribute(item,str);

       //将購物車中的商品顯示給客戶:

       out.println(" goods in your car: ");

            enumeration enum=session.getattributenames();

               { string name=(string)enum.nextelement();

                 out.print("&lt;br&gt;"+(string)session.getattribute(name));

選擇删除商品的jsp頁面

remove.jsp:

&lt;p&gt;選擇要從購物車中删除的商品:

&lt;form action="examples/servlet/removegoods" method=post name=form&gt;

&lt;input type=submit value="送出删除"&gt;

負責删除商品的servlet

removegoods.java

public class removegoods extends httpservlet

{public void init(servletconfig config) throws servletexception

public  void dopost(httpservletrequest request,httpservletresponse response)

       string item =request.getparameter("item");      //擷取要删除的商品名稱。

              session.removeattribute(item);       //删除商品。

        //将購物車中的商品顯示給客戶:

       out.println("&lt;h3&gt;now goods in your car:&lt;/h3&gt; ");

public void doget(httpservletrequest request,httpservletresponse response)

<b>7.8.3</b><b> </b><b>猜數字</b>

在第3章、第6章講述jsp内置對象以及javabeans時,曾分别舉過猜數字的例子。在這裡,我們再使用servlet來實作猜數字這個小遊戲,這樣,我們就用3種方式實作了這個小遊戲:直接由jsp頁面來實作、通過javabeans來實作、通過servlet來實作。

當客戶通路servlet:getnumber時,随機配置設定給客戶一個1到100之間的數,然後将這個數字存在客戶的session對象中。客戶在表單裡輸入一個數,來猜測配置設定給自己的那個數字。客戶輸入一個數字後,送出給servlet:result,該servlet負責判斷這個數是否和客戶的session對象中存放的那個數字相同,如果相同就連接配接到servlet:success;如果不相同就連接配接到servlet: large或small。然後,客戶在這些servlet中重新送出數字到result。

getnumber.java:

public class getnumber extends httpservlet

    {  response.setcontenttype("text/html"); 

       servletoutputstream out=response.getoutputstream();

       out.print("a number between 1 and 100 to you,guess it out please! ");

       httpsession session=request.getsession(true);

       session.setattribute("count",new integer(0));

       int number=(int)(math.random()*100)+1;        //擷取一個随機數。

       session.setattribute("save",new integer(number));

       out.print("&lt;form action=result  method=post name=form&gt;");

       out.print("&lt;input type=text name=boy &gt;");

       out.print("&lt;input type=submit value=enter&gt;");

       out.print("&lt;/form&gt;");

result.java:

public class result extends httpservlet

{ public void init(servletconfig config) throws servletexception

       string str=request.getparameter("boy");

      if(str==null)

        {str="0";

      int guessnumber=integer.parseint(str);

      integer integer=(integer)session.getattribute("save");

      int realnumber=integer.intvalue();

     if(guessnumber==realnumber)

        { int n=((integer)session.getattribute("count")).intvalue();

          n=n+1;

          session.setattribute("count",new integer(n));

           response.sendredirect("success");

       else if(guessnumber&gt;realnumber)

          response.sendredirect("larger");

    else if(guessnumber&lt;realnumber)

          response.sendredirect("smaller");

larger.java:

public class larger extends httpservlet

       out.print("larger ,try again!"); //所猜的數比實際的數大,請再猜。

       out.print("&lt;br&gt;&lt;form action=result  method=post name=form&gt;");

smaller.java

public class smaller extends httpservlet

       out.print("smaller ,try again!"); //所猜的數比實際的數小,請再猜。

success.java

public class success extends httpservlet

       int count=((integer)session.getattribute("count")).intvalue();

       int num=((integer)session.getattribute("save")).intvalue();

       long starttime=session.getcreationtime();

       long endtime=session.getlastaccessedtime();

       long spendtime=(endtime-starttime)/1000;

       out.println("congratulatuon! you are right");

       out.println("afer just"+count+"tries") ;

       out.println("you spend"+spendtime+"seconds");

       out.println("that number is"+num);