1. 程式人生 > >JAVA Web學習之servlet開發

JAVA Web學習之servlet開發

01、servlet開發入門

動態web資源開發有兩種技術:Servlet和JSP,只有把Servlet學明白了才能學JSP。

Sun公司在其API中提供了一個Servlet介面,使用者若想用一個動態web資源(即開發一個java程式向瀏覽器輸出資料),需要完成以下2個步驟:

* 編寫一個java類,實現servlet介面

* 把開發好的java類部署到web伺服器中

快速入門,用servlet向瀏覽器輸出“hello servlet”

* 閱讀servlet API ,解決兩個問題:

* 輸出hello servlet的java程式碼應該寫在servlet的那個方法內?

* 如何向IE瀏覽器輸出資料?

部分解釋:

1、service(ServletRequest req,   ServletResponse res)
          Called by the servlet container to allow the servlet to respond to  a request.

     這個方法是誰呼叫的?

     servlet是執行在伺服器上的小程式,客戶機向伺服器請求資料,伺服器呼叫這個方法,響應客戶端的請求。

2、向客戶端瀏覽器輸出資料使用response下的getOutputStream()方法。

(1) 在webapps目錄下新建一個程式資料夾,建立目錄WEB-INF,並在旗下建立classes目錄來存放java程式

(2)建立FirstServlet.java檔案,並寫 入如下程式碼:

package cn.myweb;                                                 //寫這個程式必須要有包名,並且這個程式的訪問許可權必須為public,因為你寫這個是給伺服器呼叫的

import java.io.*;                                                       //用到了輸入輸出的內容
import javax.servlet.*;                                             //用到了servlet的內容

public class FirstServlet extends GenericServlet{   //我要不就是實現servlet介面,要不就繼承他的預設實現類
 
 public void service(ServletRequest req,ServletResponse res) throws ServletException,java.io.IOException       //建議在文件中直接複製,否則大小寫出錯都不行。
  {
     OutputStream out=res.getOutputStream();
     out.write("hello servlet!!".getBytes());
  }
}

(3)在命令提示符中進入相應的目錄進行編譯   javac -d . firstservlet.java  (-d是帶包編譯的意思,.  是把編譯過的內容儲存到當前目錄下)

但是顯示javax.servlet包不存在。

為什麼找不到?

因為javac只能搜尋到j2SE相關的API來編譯程式,現在這裡用到了j2EE相關的API,這個編譯器找不到。

要把servlet API的所在的jar包加入到class path 中,讓編譯器找得到。

tomcat伺服器目錄下的lib中的servlet-api.jar加入,即set classpath=%classpath%;"c:\program file\...\servlet-api.jar"回車確定,或者直接在系統-環境變數中設定。

然後再來編譯一次。發現在WEB-INF下生成cn,cn下生成myweb,旗下生成了FirstServlet.class這個類檔案。

好的,現在servlet開發好了,我們要為他配置一個對外的訪問路徑。

對web資源的配置,通通都在web.xml中實現。

在WEB-INF下建立web.xml檔案,這個檔案怎麼寫,不知道,但是我們可以抄個類似的過來改。比如tomcat6.0下面的conf下的web.xml抄就是抄頭抄屁股。

改寫後的web.xml如下:


    <servlet>
        <servlet-name>FirstServlet</servlet-name>
        <servlet-class>cn.myweb.FirstServlet</servlet-class>
    </servlet>

    <servlet-mapping>                                                                          //將FirstServlet對映到一個對外訪問路徑上
        <servlet-name>FirstServlet</servlet-name>                               //名稱
        <url-pattern>/FirstServlet</url-pattern>                                       //對映到web應用的這個路徑上
    </servlet-mapping>

</web-app>

顯示成功,完成!

02 、servlet的呼叫過程和生命週期

開啟UML繪圖軟體 Rational Rose,畫出servlet的呼叫過程

為什麼不把servlet的destroy方法畫出來?這是與servlet的生命週期有關的

生命週期:什麼時候生,什麼時候死,在生和死之間有什麼事情會發生。

servlet什麼時候生,客戶機第一次訪問的時候,伺服器會建立這個servlet,伺服器一旦把這個servlet物件建立起來,他就一直駐留在記憶體裡面了,等待客戶端的第二次訪問,     而不會立即destroy。

servlet什麼時候被摧毀,web伺服器停止的時候,或者這個web應用被從伺服器裡刪除的時候。

servlet的執行過程:

Servlet程式是由WEB伺服器呼叫,web伺服器收到客戶端的servlet訪問請求後:

(1)Web伺服器首先檢查是否已經裝載並建立了該servlet例項物件。如果是則直接執行第四步,否則執行第二步。

(2)裝載並建立該servlet例項物件。

(3)呼叫servlet例項物件的init()方法。

(4)建立一個用於封裝HTTP請求訊息的HttpServletRequest物件和一個代表HTTP響應訊息的HttpServletResponse物件,然後呼叫servlet的service()方法,並將請求和響應物件作為引數傳遞進去。(伺服器發現response物件中有內容,就回送給客戶端)

(5)Web應用程式被停止或重新啟動之前,Servlet引擎將解除安裝servlet,並在解除安裝之前呼叫servlet的destroy()方法。

03、使用eclipse開發servlet

在eclipse中新建一個web project工程,eclipse會自動建立下圖所示的目錄結構:

在src中寫程式,工程在釋出的時候會自動把寫的程式編譯併發布到WEB-INF下的classes目錄下去。

Java EE 5.0和J2EE 1.4 ,Java EE 5.0是新版本,採用了新的命名方法,其中加入了許多開發包,J2EE 1.4相對純潔些,比Java EE 5.0少些東西,做練習時我們選J2EE 1.4。

開發步驟:

(1)src下新建個class

package cn.mytest;

import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MyServlet1 extends GenericServlet {

 @Override
 public void service(ServletRequest arg0, ServletResponse arg1)         
   throws ServletException, IOException {
  // TODO Auto-generated method stub

 }

}

注意:MyServlet1.java中自動生成的service()方法中的引數不是req和res,這是沒有導原碼的原因,

在ServletRequest上右鍵open declaration》Attach Source 匯入你的tomcat6.0 src檔案,然後點選MyServlet1,再實現以下這個方法,就自動變成了req和res

(2)向瀏覽器輸出資料:res.getOutputStream().write("hello my first servlet!".getBytes());

(3)在web.xml 中給servlet配置一個對外訪問路徑

 <servlet>
   <servlet-name>MyServlet1</servlet-name>
    <servlet-class>cn.mytest.MyServlet1</servlet-class>
  </servlet>
 
  <servlet-mapping>
     <servlet-name>MyServlet1</servlet-name>
     <url-pattern>/MyServlet1</url-pattern>
  </servlet-mapping>

(4)將此servlet程式釋出到伺服器上去

現在myeclipse裡面整合tomcat,myeclipse》window》preferences》MyEclipse Enterprise Work》servers》Tomcat》Tomcat 6.X

選擇好目錄,並勾選上面的Enable。

注意:tomcat是一個java程式,我們沒有指定具體是哪個java虛擬機器來跑這個程式。點開tomcat 6.x,下面有個JDK,預設為MyEclipse,一般來說不用改。Launch要處於debug node。

(5)可以將工程釋出到你剛才整合的tomcat中去了

點選工具欄上的Deploy MyEclipse J2EE Project to server(部署J2EE工程到伺服器),點選Add,增加一臺伺服器,選擇tomcat。

(6)啟動tomcat來訪問剛才寫好的程式

點選工具欄上的Run/Stop/Restart MyEclipse server 下拉列表,選擇tomcat。

成功!

這是種比較笨的方法,可以在src右鍵直接建個Servlet,使用這個模板,他會直接把servlet寫好同時幫你部署好web.xml。

注:如果類名myservlet寫錯了,在pack區的類名上右鍵選擇refactor>rename,則所有引用都更新!!但是eclipse不夠聰明,web.xml沒有更新,必須要手動改一下!

04、HttpServlet基礎

Servlet介面實現類:

Servlet介面Sun公司定義了兩個預設實現類,分別為:GenericServlet、HttpServlet。

HttpServlet指能夠處理HTTP請求的servlet,它在原有servlet介面上添加了一些與HTTP協議處理方法,它比servlet介面的功能更為強大。因此開發人員在編寫servlet時,通常應繼承這個類,而避免直接去實現servlet介面。

HttpServlet在實現servlet介面時,覆寫了service方法,該方法體內的程式碼會自動判斷使用者的請求方式,如為GET請求方式,則呼叫HttpServlet的doGet方法,如為POST請求時,則呼叫doPost方法,因此,開發人員在編寫servlet時,通常只需要覆寫doGet或doPost方法,而不要去覆寫service方法。

預設生成的HttpServlet()方法,裡面沒用的說明太多,如何更改httpservlet()的模板?

在myeclipse的安裝目錄下搜尋:Servlet.java檔案,開啟,修改doget、dopost等方法,刪掉沒用的說明內容。

05、servlet開發的一些重要細節

(1)由於客戶端是通過URL地址來訪問web伺服器中的資源,所以servlet程式若想被外界訪問,必須把servlet程式對映到一個URL地址上,這個工作在web.xml檔案中使用<servlet>元素和<servlet-mapping>元素來完成。

<servlet>元素用於註冊servlet,它包含有兩個主要的子元素:<servlet-name>和<servlet-class>,分別用於設定servlet的註冊名稱和servlet的完整類名。

一個<servlet-mapping>元素用於對映一個已註冊的servlet的一個對外訪問路徑,它包含有兩個子元素:<servlet-name>和<url-pattern>,分別用於指定servlet的註冊名和servlet的對外訪問路徑。

同一個servlet,可以有多個<servlet-mapping>來對映多個URL來訪問,地址上也可以使用萬用字元*,但是隻能有兩種固定的格式:

一種格式是*.副檔名

一種格式是以/開頭,以/*結尾。

不能寫成混合體   /XXX/*.html

注:web.xml改動,不需要重啟伺服器,即會自動重新載入(但是寫java類的時候,一般都要重新載入)。

因為conf下的context.xml中配置的是全域性的設定,被所有web應用共享,裡面有句<WatchedResource>WEB-INF/web.xml</WatchedResource>

<url-pattern>/aa</url-pattern>

<url-pattern>/1.html</url-pattern>

因為使用了萬用字元,所以就可能會有地址衝突的問題:

例如,對於如下的對映關係:

。servlet1 對映到/abc/*

。servlet2 對映到/*

。servlet3 對映到/abc

。servlet4 對映到*.do

問題:

。當請求的URL為/abc/a.html,/abc/*和/*都匹配,到底哪個servlet響應呢?     應為:servlet1

。當請求的URL為/abc時,/*和/abc都匹配,到底哪個servlet響應呢?               應為:servlet3

。當請求的URL為/abc/a.do時,/abc/*和/*.do都匹配,到底哪個servlet響應呢?應為:servlet1

。當請求的URL為/a.do時,/*和*.do都匹配,到底哪個servlet響應呢?               應為:servlet2

。當請求的URL為/XXX/yyy/a.do時,/*和*.do 到底哪個servlet響應呢?             應為:servlet2

從第一個字母比較,哪個最像,最接近就是哪個響應。*.do的優先順序是最低的。

(2)servlet是一個供其他java程式(servlet引擎)呼叫的java類,他不能獨立執行,他的執行完全由servlet引擎來控制和排程。

針對客戶端的多次servlet請求,通常情況下,伺服器只會一個servlet例項物件,也就是說servlet例項物件一旦建立,他就會駐留在記憶體中,為後續的其他請求服務,直至web容器退出,servlet例項物件才會銷燬。

在servlet的整個生命週期中,servlet的init()方法只被呼叫一次(物件建立時才呼叫,第一次訪問servlet程式時呼叫)。而對一個servlet的每次訪問請求都會導致servlet引擎呼叫一次servlet的service()方法。

對於每一次訪問請求(一個人發10次請求,也是10個request),servlet引擎都會建立一個新的HttpServletRequest請求物件和一個新的HttpServletResponse響應物件,然後將這兩個物件作為引數傳遞給它呼叫的servlet的service()方法,service()方法再根據請求方式分別呼叫doGet/doPost方法。

請求結束,request和response會立刻銷燬釋放,所以像新浪搜狐每天上億次的請求,也不怕,基本不佔記憶體空間。

(3)如果在<servlet>元素中配置了一個<load-on-startup>元素,那麼WEB應用程式在啟動時,就會裝載並建立servlet的例項物件、以及呼叫servlet例項物件的init()方法。

如果不配置的話,伺服器接受第一次訪問請求時才建立servlet的例項物件,才呼叫init()方法。

舉例:

<servlet>

      <servlet-name>invoker</servlet-name>

      <servlet-class>

               org.apache.catalina.servlets.InvokerServlet

      </servlet-class>

      <load-on-startup>2 </load-on-startup>

</servlet>

注:<load-on-startup>2</load-on-startup>  中數字的大小決定了啟動的順序。數字越小,優先順序越高。

用途:為web應用寫一個InitServlet,這個servlet配置為啟動時裝載,為整個web應用建立必要的資料庫表和資料。

(4)如果某個servlet的對映路徑僅僅為一個正斜槓/,那麼這個servlet就成為當前web應用程式的預設servlet。

凡是在web.xml檔案中找不到匹配的<servlet-mapping>元素的URL,他們的訪問請求都將交給預設servlet處理,也就是說,預設servlet用於處理所有其他servlet都不處理的請求訪問。比如訪問/day05/shifdfadjfaohfashfoafaoj,沒有這個路徑,自動訪問預設的servlet。

在<tomcat的安裝目錄>\cconf\web.xml檔案中,註冊了一個名稱為org.apache.catalina.servlet.DefaultServlet的servlet,並將這個servlet設定為預設servlet。

當訪問Tomcat伺服器中的某個靜態HTML檔案和圖片時,實際上是在訪問這個預設servlet。

假如你在WebRoot下新建個1.html,訪問/day05/1.html,是由預設servlet來執行的。

記住:瀏覽器向伺服器傳送請求,不管你傳送的是什麼請求,靜態的,或者動態的頁面,都是去找servlet。

對1.html傳送請求,沒有servlet對映到這個地址上來,這時候伺服器發現沒有servlet伺服器對映到這個地址,他就去找預設的servlet。由預設的servlet來查詢名稱為1.html的靜態頁面資源,若找到,則其讀取1.html的資料。

開啟web.xml,發現裡面沒有帶/的預設servlet配置,但是雖然沒有明確寫,但是伺服器幫你配置了一個預設的預設servlet。

如果我們自己寫一個預設的servlet,就會把伺服器的預設servlet給覆蓋掉。

<servlet-mapping>

    <servlet-name>firstweb</servlet-name>

    <url-pattern>/</url-pattern>

</servlet-mapping>

注意當你在瀏覽器中隨便輸入一個不存在的地址,比如/day05/djfaojfwjoejfjeowjf,瀏覽器會顯示404,這個頁面也是資料啊,也是由程式輸出的。

誰輸出的呢?還是預設servlet,他找這個名稱的靜態頁面資源,發現還是找不到,就給你回送找不到,404。

伺服器背後這個預設的servlet到底在哪呢?  在\conf\web.xml中。(這裡面配置的web資源,由所有的web伺服器共享!)

<servlet>

     <servlet-name>default</servlet-name>

     <servlet-class>org.apache.catalina.servlet.DefaultServlet</servlet-class>

     ......

     <load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

     <servlet-name>default</servlet-name>

     <url-pattern>/</url-pattern>

</servlet-mapping>

06、servlet的執行緒安全問題

當多個客戶端併發訪問同一個servlet時,web伺服器會為每一個客戶端的訪問請求建立一個執行緒,並在這個執行緒上呼叫servlet的service()方法,因此service()方法內如果訪問了同一個資源的話,就有可能引發執行緒安全問題。

如果100個執行緒同時執行下面程式碼,會產生執行緒安全問題嗎?

public class MyServlet3 extends GenericServlet {
 public void service(ServletRequest req, ServletResponse res)   throws ServletException, IOException {  
  int i=0;
  i++;
  System.out.println(i);    
 }

每個執行緒有各自的i,所以不會有執行緒安全問題。

若改為

public class MyServlet3 extends GenericServlet {

int i=0;
 public void service(ServletRequest req, ServletResponse res)   throws ServletException, IOException {  
  i++;
  System.out.println(i);   
}

則會有執行緒安全問題,因為i,只有一個,100個執行緒同時來操作這同一個i。

執行緒安全問題,一般是指程式操作共享資源,比如檔案,資料庫,共享變數等。

若改為:

public class MyServlet3 extends GenericServlet {

 int i=0;
 public void service(ServletRequest req, ServletResponse res)
   throws ServletException, IOException {  
  synchronized(this)
  {
   i++;
   try {
    Thread.sleep(1000*5);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }   
   System.out.println(i);   
   res.getOutputStream().write((i+"").getBytes());
  }  
 }

}

則可以正確的順序輸出,因為當兩個併發執行緒訪問同一個物件中的這個synchronized(this)同步程式碼塊時,同一時間內只能有一個執行緒得到執行。另一個執行緒必須等待當前執行緒執行完這個程式碼塊以後才能執行該程式碼塊。
但是在實際開發中,不能這麼實現,因為一個使用者的訪問,就阻斷其餘所有的使用者訪問,這麼做不現實。

servlet如何來解決這個問題呢?使用SingleThreadModel介面,這個接口裡面啥都沒有,沒有定義任何方法,我們通常稱為標記介面。

只要在Servlet類的定義中增加實現SingleThreadModel介面的宣告即可,如果你的類實現了這個介面,就相當於你這個類打上了標號,即你的這個類就與眾不同了,擁有了一些特定的功能。

你的類如果實現了SingleThreadModel介面,那麼你這個類就是執行緒安全的。

public class MyServlet3 extends HttpServlet implements SingleThreadMdoel{}

如果某個servlet實現了SingleThreadModel介面,那麼Servlet引擎將以單執行緒模式來呼叫其service()方法。

對於實現了SingleThreadModel介面的Servlet,Servlet引擎仍然支援對該Servlet的多執行緒併發訪問,其採用的方式是產生多個servlet例項物件,併發的每個執行緒分別呼叫一個獨立的servlet例項物件,一個servlet物件對應一個請求。

實現SingleThreadModel介面並不能真正解決servlet的執行緒安全問題,因為servlet引擎會建立多個Servlet例項物件,而真正意義上解決多執行緒安全問題是指一個Servlet例項物件被多個執行緒同時呼叫的問題。事實上,在Servlet API 2.4中,已經將SingleThreadModel標記為Deprecated(過時的)。

07、SrvletConfig物件和它在開發中的應用場景

web伺服器在呼叫servlet時到底傳多少個物件呢?開啟servlet文件,左側N多的API,如果你把這些都掌握了,也就學好了。

servletConfig物件

在servlet的配置檔案中,可以使用一個或多個<init-param>標籤為servlet配置一些初始化引數。

當servlet配置了初始化引數後,web容器在建立servlet例項物件時,伺服器會自動將這些初始化引數封裝到ServletConfig物件中,並在呼叫servlet的init方法時,將ServletConfig物件傳遞給servlet。進而,程式設計師通過ServletConfig物件就可以得到當前Servlet的初始化引數資訊。

例如,程式通常需要一些資料,但是這些資料寫實在程式中又不太好,我們可以寫在配置檔案中:

<servlet>
    <servlet-name>ServletConfigtest</servlet-name>
    <servlet-class>cn.mytest.ServletConfigtest</servlet-class>
   
    <init-param>
     <param-name>data</param-name>
     <param-value>datadevalue</param-value>
    </init-param>

  </servlet>

注:若有多個數據可以寫多個<init-param>
在配置檔案配置上了這些資訊後,伺服器會自動把這些配置資訊封裝到servletConfig物件當中。並在呼叫servlet的init()方法時,將ServletConfig物件傳遞給servlet  。我要在servlet中拿到資料,只需要覆寫init()方法。

public class ServletConfigtest extends HttpServlet {
 private ServletConfig config;
 public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {

  String value=config.getInitParameter("data");
  System.out.println(value);
 } 

 public void init(ServletConfig config) throws ServletException {
  // TODO Auto-generated method stub
  this.config=config;
 }  
}

因為httpServlet的父類中已經定義過方法,所以可以簡化為:

public class myconfig extends HttpServlet {

 public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {

  String value=this.getServletConfig().getInitParameter("data");
  System.out.println(value);
 }
 
 public void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  doGet(request,response);
 }
}

若在web.xml中配置多個數據,則

public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {

  //得到指定的
  String value=this.getServletConfig().getInitParameter("data");
  System.out.println(value);
  //得到所有的
  Enumeration e=this.getServletConfig().getInitParameterNames();
  while(e.hasMoreElements()){
   String name=(String)e.nextElement();
   String value1=this.getServletConfig().getInitParameter(name);
   System.out.println(name+"="+value1);
  }   
 }

那麼在實際開發中,什麼資料不適合寫實,適合在servletconfig裡面配置呢?

比如配置中文顯示的字符集編碼;

比如連線那個資料庫,及資料庫名和密碼;

比如servlet具體讀取那個配置檔案,(struts中常用)。

08、ServletContext的介紹和應用

Web容器在啟動時,它會為每個Web應用程式都建立一個對應的ServletContext物件,它代表當前web應用。(物件有相應的方法,及servletContext一定有管理所有web資源的方法,及也可以實現資源間的資料傳遞和共享。)

ServletConfig物件中維護了ServletContext物件的引用,開發人員在編寫servlet時,可以通過ServletConfig.getServletContext方法獲得ServletContext物件。

如:

public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  //得到ServletContext的方法1
  ServletContext context = this.getServletConfig().getServletContext();
  
  //得到ServletContext的方法2
  context=this.getServletContext();
   
 }

由於一個Web應用中的所有Servlet共享同一個ServletContext物件,因此Servlet物件之間可以通過servletcontext物件來實現資料共享。servletcontext物件通常也被稱之為context物件。(web發開中總共四個域,context、request、session、page域,難點!)

context域,

1、這是一個容器;

2、說明了這個容器的作用範圍,也就是應用程式範圍。

如下例子實現兩個servlet共享傳遞資料:

在servletcontext1中寫入:

public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {

  String data="xyz";
  this.getServletContext().setAttribute("data", data);  
 }

在servletcontext2中寫入:

 public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {

  String a=(String)this.getServletContext().getAttribute("data");
  System.out.println(a);
  
 }

然後現在瀏覽器中訪問1的URL,然後再訪問2的URL,即可在console中收到顯示的資料。

實際專案開發中,可以用在如聊天室中,兩個人間的資料傳遞。

可以獲取Web應用的初始化引數

在web.xml中,寫

<context-param>

    <param-name>data</param-name>

    <param-value>XXX</param-value>

</context-param>

<context-param>為整個web應用配置初始化引數。<init-param>只是為某個具體的servlet配置初始化引數。

web伺服器在載入這個應用的時候,就建立代表這個應用的servletcontext物件,這個物件創建出來了之後,他會自動把這個配置的初始化引數,封裝到servlet的context物件當中。你在程式中直接取就可以了。

 public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  String value=this.getServletContext().getInitParameter("data");
  System.out.println(value);
 }

多個初始化引數,可以參考上面的設定。

servlet如何連線資料庫?如果有100個servlet都要連線資料庫,是不是每個都要配置servletconfig?

可以用<context-param>來配置整個web應用的初始化引數,統一配置資料庫的連線。

可以實現servlet的轉發

轉發,你請求A,A沒有,A幫你去找。客戶機只發了一次請求。

重定向是,你請求A,A沒有,A讓你自己去找。客戶機發了兩次請求。

轉發在web開發中很常用,產生的資料,轉發給JSP,讓JSP去美化,生成HTML輸出。

例如,新建servlet

public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  String data="aaaaaaaaaaaaa";
  //把資料存在servletcontext中帶給1.jsp,可以實現但是是不合理的。
  this.getServletContext().setAttribute("data", data);                                         
  
  RequestDispatcher rd=this.getServletContext().getRequestDispatcher("/1.jsp");
  rd.forward(request, response);
 }

在WebRoot目錄下新建1.jsp

<body>
    <h1>
    <font color="red">
    <%
     String value=(String)application.getAttribute("data");              //application就代表servletcontext
     out.write(value);
     %>     
     </font>
     </h1>
  </body>

釋出後,在瀏覽器位址列上寫上servlet的對映地址,既可以輸出排版後的資料aaaaaaaaaaaaa。這就是請求轉發,servlet去請求1.jsp然後反饋給客戶端瀏覽器。

將資料儲存在servletcontext中帶給1.jsp是不合理的,因為servletcontext是被所有人共享的。比如有很多人同時來訪問,他們的資料都儲存在data,第一個使用者的資料可能沒來得及轉發給1.jsp去顯示,就被第二個使用者給覆蓋掉了,結果把第二個使用者的資料給顯示出來了。

那麼servlet的資料由誰給帶給jsp呢?不能通過context域,要通過request域,且見下回分解。

servletContext物件的生命週期

建立,伺服器啟動的時候,會對伺服器裡面的每一個應用分別建立一個servletContext。webapps裡面有多少個工程,就建立多少個servletContext。

銷燬,停掉伺服器,或者從伺服器裡刪除某個web應用。

09、利用servletContext物件來讀取web應用中的資原始檔

比如,應用程式中有許多的servlet都要連線資料庫,到底連線那個庫,我們可以使用一個配置檔案來儲存。

在軟體開發裡面,用作配置檔案的檔案型別通常有兩種,一種是xml檔案,一種是properties

如果配置檔案中的資料間是有關係的,則用xml檔案,反之使用properties

連線資料庫的是沒有關係的,所以使用properties。在src上右鍵new>file>db.properties。

url=jdbc:mysql://localhost:3306/test
username=root
password=root

程式要連線資料庫,需要讀取這個配置檔案,這個檔案在web工程裡面也是屬於web資源,可以通過servletContext來讀取。

 新建一個servlet,

public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {

   //InputStream in=this.getServletContext().getResourceAsStream("/src/db.properties");  //這樣寫地址是錯的,/代表web工程,src在釋出到伺服器時會寫到classes檔案下面。

    InputStream in=this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");//這樣的地址才是正確的


//下面是模板程式碼,讀取properties檔案通通都是這麼讀,要求熟練掌握
  Properties pros=new Properties();
  pros.load(in);
  
  String url=pros.getProperty("url");
  String username=pros.getProperty("username");
  String password=pros.getProperty("password");
  
  System.out.println(url);
  System.out.println(username);
  System.out.println(password);

 }

在瀏覽器裡輸入當前servlet的URL,即可在myeclipse的console裡面顯示出properties對應的資訊。

注:傳統的檔案讀取方式為:FileInputStream in=new FileInputStream(“classes/db.properties”);

他的相對路徑是相對於java 虛擬機器的目錄的,即bin目錄。

所以一般不使用這種方式,使用servletcontext就可以避免這個問題。

 也可以通過servletcontext的getRealPath方法,先獲得絕對路徑,然後再呼叫傳統的FileInputStream。

String path=this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");//獲得檔案的絕對路徑
  FileInputStream in=new FileInputStream(path);  

  String filename=path.substring(path.lastIndexOf("\\")+1);               //獲得檔案的真實名稱
  //下面是模板程式碼,讀取properties檔案通通都是這麼讀,要求熟練掌握
  Properties pros=new Properties();
  pros.load(in);
  
  String url=pros.getProperty("url");
  String username=pros.getProperty("username");
  String password=pros.getProperty("password");
  
  System.out.println(url);
  System.out.println(username);
  System.out.println(password);
  System.out.println(filename);

這種方式一般用來做下載用,因為他可以獲得真實名稱,而第一種方法,只能得到流。 

使用者請求servlet,當需要操作資料庫時,有專門的dao程式來負責操作資料庫,servlet不去操作資料庫,要去呼叫dao,來操作資料庫。

如果讀取資原始檔不是servlet,只是普通的java程式的話,就只能通過類裝載器來讀。比如在cn.myweb.dao下新建一個UserDao.java,就不能直接操作servletcontext。

 InputStream in= UserDao.class.getClassLoader().getResourceAsStream("db.properties");

但是用類載入器載入的檔案會到記憶體中,所以檔案不能太大。

注意:使用類裝載器,他只裝載一次進入記憶體,當你修改檔案後,再次訪問時,他發現記憶體中已經有這個類了,就直接從記憶體讀取,即讀取的仍然是舊的檔案。

解決辦法,不能通過類載入的方式來讀,傳統方法來讀,需要檔案的路徑,所以我們通過類載入獲得路徑後再使用傳統方法來讀取。這樣就可以及時讀取檔案的更新了。

  String path=UserDao.class.getClassLoader().getResource("db.properties").getPath();
  FileInputStream in=new FileInputStream(path);
  
  Properties pros=new Properties();
  pros.load(in);
  
  System.out.println(pros.getProperty("url"));