javaweb總結(5) - servlet開發

一、Servlet簡介

  Servlet是sun公司提供的一門用於開發動態web資源的技術。
  Sun公司在其API中提供了一個servlet接口,用戶若想用發一個動態web資源(即開發一個Java程序向瀏覽器輸出數據),需要完成以下2個步驟:
  1、編寫一個Java類,實現servlet接口。
  2、把開發好的Java類部署到web服務器中。
  按照一種約定俗成的稱呼習慣,通常我們也把實現了servlet接口的java程序,稱之爲Servlet

二、Servlet的運行過程

Servlet程序是由WEB服務器調用,web服務器收到客戶端的Servlet訪問請求後:
  ①Web服務器首先檢查是否已經裝載並創建了該Servlet的實例對象。如果是,則直接執行第④步,否則,執行第②步。
  ②裝載並創建該Servlet的一個實例對象。 
  ③調用Servlet實例對象的init()方法。
  ④創建一個用於封裝HTTP請求消息的HttpServletRequest對象和一個代表HTTP響應消息的HttpServletResponse對象,然後調用Servlet的service()方法並將請求和響應對象作爲參數傳遞進去。
  ⑤WEB應用程序被停止或重新啓動之前,Servlet引擎將卸載Servlet,並在卸載之前調用Servlet的destroy()方法。 

三、Servlet調用圖

 Servlet調用圖

四、在Eclipse中開發Servlet

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

 

4.1、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方法。

4.2、通過Eclipse創建和編寫Servlet

  選中gacl.servlet.study包,右鍵→New→Servlet,如下圖所示:

  

  

  

  這樣,我們就通過Eclipse幫我們創建好一個名字爲ServletDemo1的Servlet,創建好的ServletDemo01裏面會有如下代碼:

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServlet;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 public class ServletDemo1 extends HttpServlet {
12 
13     /**
14      * The doGet method of the servlet. <br>
15      *
16      * This method is called when a form has its tag value method equals to get.
17      * 
18      * @param request the request send by the client to the server
19      * @param response the response send by the server to the client
20      * @throws ServletException if an error occurred
21      * @throws IOException if an error occurred
22      */
23     public void doGet(HttpServletRequest request, HttpServletResponse response)
24             throws ServletException, IOException {
25 
26         response.setContentType("text/html");
27         PrintWriter out = response.getWriter();
28         out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
29         out.println("<HTML>");
30         out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
31         out.println("  <BODY>");
32         out.print("    This is ");
33         out.print(this.getClass());
34         out.println(", using the GET method");
35         out.println("  </BODY>");
36         out.println("</HTML>");
37         out.flush();
38         out.close();
39     }
40 
41     /**
42      * The doPost method of the servlet. <br>
43      *
44      * This method is called when a form has its tag value method equals to post.
45      * 
46      * @param request the request send by the client to the server
47      * @param response the response send by the server to the client
48      * @throws ServletException if an error occurred
49      * @throws IOException if an error occurred
50      */
51     public void doPost(HttpServletRequest request, HttpServletResponse response)
52             throws ServletException, IOException {
53 
54         response.setContentType("text/html");
55         PrintWriter out = response.getWriter();
56         out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
57         out.println("<HTML>");
58         out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
59         out.println("  <BODY>");
60         out.print("    This is ");
61         out.print(this.getClass());
62         out.println(", using the POST method");
63         out.println("  </BODY>");
64         out.println("</HTML>");
65         out.flush();
66         out.close();
67     }
68 
69 }
複製代碼

  這些代碼都是Eclipse自動生成的,而web.xml文件中也多了<servlet></servlet>和<servlet-mapping></servlet-mapping>兩對標籤,這兩對標籤是配置ServletDemo1的,如下圖所示:

然後我們就可以通過瀏覽器訪問ServletDemo1這個Servlet,如下圖所示:

  

五、Servlet開發注意細節

5.1、Servlet訪問URL映射配置

  由於客戶端是通過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的對外訪問路徑。例如:

複製代碼
1   <servlet>
2     <servlet-name>ServletDemo1</servlet-name>
3     <servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
4   </servlet>
5 
6   <servlet-mapping>
7     <servlet-name>ServletDemo1</servlet-name>
8     <url-pattern>/servlet/ServletDemo1</url-pattern>
9   </servlet-mapping>
複製代碼

  同一個Servlet可以被映射到多個URL上,即多個<servlet-mapping>元素的<servlet-name>子元素的設置值可以是同一個Servlet的註冊名。 例如:

複製代碼
 1  <servlet>
 2     <servlet-name>ServletDemo1</servlet-name>
 3     <servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
 4   </servlet>
 5 
 6   <servlet-mapping>
 7     <servlet-name>ServletDemo1</servlet-name>
 8     <url-pattern>/servlet/ServletDemo1</url-pattern>
 9   </servlet-mapping>
10  <servlet-mapping>
11     <servlet-name>ServletDemo1</servlet-name>
12     <url-pattern>/1.htm</url-pattern>
13   </servlet-mapping>
14    <servlet-mapping>
15     <servlet-name>ServletDemo1</servlet-name>
16     <url-pattern>/2.jsp</url-pattern>
17   </servlet-mapping>
18    <servlet-mapping>
19     <servlet-name>ServletDemo1</servlet-name>
20     <url-pattern>/3.php</url-pattern>
21   </servlet-mapping>
22    <servlet-mapping>
23     <servlet-name>ServletDemo1</servlet-name>
24     <url-pattern>/4.ASPX</url-pattern>
25   </servlet-mapping>
複製代碼

  通過上面的配置,當我們想訪問名稱是ServletDemo1的Servlet,可以使用如下的幾個地址去訪問:

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/servlet/ServletDemo1

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/1.htm

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/2.jsp

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/3.php

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/4.ASPX

  ServletDemo1被映射到了多個URL上。

5.2、Servlet訪問URL使用*通配符映射  

在Servlet映射到的URL中也可以使用*通配符,但是只能有兩種固定的格式:一種格式是"*.擴展名",另一種格式是以正斜槓(/)開頭並以"/*"結尾。例如:

  

複製代碼
1  <servlet>
2     <servlet-name>ServletDemo1</servlet-name>
3     <servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
4   </servlet>
5 
6    <servlet-mapping>
7     <servlet-name>ServletDemo1</servlet-name>
8    <url-pattern>/*</url-pattern>
複製代碼

  *可以匹配任意的字符,所以此時可以用任意的URL去訪問ServletDemo1這個Servlet,如下圖所示:

  

對於如下的一些映射關係:
  Servlet1 映射到 /abc/* 
  Servlet2 映射到 /* 
  Servlet3 映射到 /abc 
  Servlet4 映射到 *.do 
問題:
  當請求URL爲“/abc/a.html”,“/abc/*”和“/*”都匹配,哪個servlet響應
      Servlet引擎將調用Servlet1。
  當請求URL爲“/abc”時,“/abc/*”和“/abc”都匹配,哪個servlet響應
      Servlet引擎將調用Servlet3。
  當請求URL爲“/abc/a.do”時,“/abc/*”和“*.do”都匹配,哪個servlet響應
      Servlet引擎將調用Servlet1。
  當請求URL爲“/a.do”時,“/*”和“*.do”都匹配,哪個servlet響應
      Servlet引擎將調用Servlet2。
  當請求URL爲“/xxx/yyy/a.do”時,“/*”和“*.do”都匹配,哪個servlet響應
      Servlet引擎將調用Servlet2。
  匹配的原則就是"誰長得更像就找誰"

5.3、Servlet與普通Java類的區別  

  Servlet是一個供其他Java程序(Servlet引擎)調用的Java類,它不能獨立運行,它的運行完全由Servlet引擎來控制和調度。
  針對客戶端的多次Servlet請求,通常情況下,服務器只會創建一個Servlet實例對象,也就是說Servlet實例對象一旦創建,它就會駐留在內存中,爲後續的其它請求服務,直至web容器退出,servlet實例對象纔會銷燬。
  在Servlet的整個生命週期內,Servlet的init方法只被調用一次。而對一個Servlet的每次訪問請求都導致Servlet引擎調用一次servlet的service方法。對於每次訪問請求,Servlet引擎都會創建一個新的HttpServletRequest請求對象和一個新的HttpServletResponse響應對象,然後將這兩個對象作爲參數傳遞給它調用的Servlet的service()方法,service方法再根據請求方式分別調用doXXX方法。

  如果在<servlet>元素中配置了一個<load-on-startup>元素,那麼WEB應用程序在啓動時,就會裝載並創建Servlet的實例對象、以及調用Servlet實例對象的init()方法。
    舉例:
    <servlet>
        <servlet-name>invoker</servlet-name>
        <servlet-class>
            org.apache.catalina.servlets.InvokerServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

  用途:爲web應用寫一個InitServlet,這個servlet配置爲啓動時裝載,爲整個web應用創建必要的數據庫表和數據。

5.4、缺省Servlet

  如果某個Servlet的映射路徑僅僅爲一個正斜槓(/),那麼這個Servlet就成爲當前Web應用程序的缺省Servlet。 
  凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它們的訪問請求都將交給缺省Servlet處理,也就是說,缺省Servlet用於處理所有其他Servlet都不處理的訪問請求。 例如:

複製代碼
 1  <servlet>
 2     <servlet-name>ServletDemo2</servlet-name>
 3     <servlet-class>gacl.servlet.study.ServletDemo2</servlet-class>
 4     <load-on-startup>1</load-on-startup>
 5   </servlet>
 6   
 7   <!-- 將ServletDemo2配置成缺省Servlet -->
 8   <servlet-mapping>
 9     <servlet-name>ServletDemo2</servlet-name>
10     <url-pattern>/</url-pattern>
11   </servlet-mapping>
複製代碼

  當訪問不存在的Servlet時,就使用配置的默認Servlet進行處理,如下圖所示:

  

  在<tomcat的安裝目錄>\conf\web.xml文件中,註冊了一個名稱爲org.apache.catalina.servlets.DefaultServlet的Servlet,並將這個Servlet設置爲了缺省Servlet。

複製代碼
 1     <servlet>
 2         <servlet-name>default</servlet-name>
 3         <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
 4         <init-param>
 5             <param-name>debug</param-name>
 6             <param-value>0</param-value>
 7         </init-param>
 8         <init-param>
 9             <param-name>listings</param-name>
10             <param-value>false</param-value>
11         </init-param>
12         <load-on-startup>1</load-on-startup>
13     </servlet>
14 
15  <!-- The mapping for the default servlet -->
16     <servlet-mapping>
17         <servlet-name>default</servlet-name>
18         <url-pattern>/</url-pattern>
19     </servlet-mapping>
複製代碼

  當訪問Tomcat服務器中的某個靜態HTML文件和圖片時,實際上是在訪問這個缺省Servlet。

5.5、Servlet的線程安全問題

  當多個客戶端併發訪問同一個Servlet時,web服務器會爲每一個客戶端的訪問請求創建一個線程,並在這個線程上調用Servlet的service方法,因此service方法內如果訪問了同一個資源的話,就有可能引發線程安全問題。例如下面的代碼:

不存在線程安全問題的代碼:

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 public class ServletDemo3 extends HttpServlet {
11 
12     
13     public void doGet(HttpServletRequest request, HttpServletResponse response)
14             throws ServletException, IOException {
15         
16         /**
17          * 當多線程併發訪問這個方法裏面的代碼時,會存在線程安全問題嗎
18          * i變量被多個線程併發訪問,但是沒有線程安全問題,因爲i是doGet方法裏面的局部變量,
19          * 當有多個線程併發訪問doGet方法時,每一個線程裏面都有自己的i變量,
20          * 各個線程操作的都是自己的i變量,所以不存在線程安全問題
21          * 多線程併發訪問某一個方法的時候,如果在方法內部定義了一些資源(變量,集合等)
22          * 那麼每一個線程都有這些東西,所以就不存在線程安全問題了
23          */
24         int i=1;
25         i++;
26         response.getWriter().write(i);
27     }
28 
29     public void doPost(HttpServletRequest request, HttpServletResponse response)
30             throws ServletException, IOException {
31         doGet(request, response);
32     }
33 
34 }
複製代碼

存在線程安全問題的代碼:

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 public class ServletDemo3 extends HttpServlet {
11 
12     int i=1;
13     public void doGet(HttpServletRequest request, HttpServletResponse response)
14             throws ServletException, IOException {
15         
16         i++;
17         try {
18             Thread.sleep(1000*4);
19         } catch (InterruptedException e) {
20             e.printStackTrace();
21         }
22         response.getWriter().write(i+"");
23     }
24 
25     public void doPost(HttpServletRequest request, HttpServletResponse response)
26             throws ServletException, IOException {
27         doGet(request, response);
28     }
29 
30 }
複製代碼

  把i定義成全局變量,當多個線程併發訪問變量i時,就會存在線程安全問題了,如下圖所示:同時開啓兩個瀏覽器模擬併發訪問同一個Servlet,本來正常來說,第一個瀏覽器應該看到2,而第二個瀏覽器應該看到3的,結果兩個瀏覽器都看到了3,這就不正常。

  

  線程安全問題只存在多個線程併發操作同一個資源的情況下,所以在編寫Servlet的時候,如果併發訪問某一個資源(變量,集合等),就會存在線程安全問題,那麼該如何解決這個問題呢?

先看看下面的代碼:

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 
11 public class ServletDemo3 extends HttpServlet {
12 
13     int i=1;
14     public void doGet(HttpServletRequest request, HttpServletResponse response)
15             throws ServletException, IOException {
16         /**
17          * 加了synchronized後,併發訪問i時就不存在線程安全問題了,
18          * 爲什麼加了synchronized後就沒有線程安全問題了呢?
19          * 假如現在有一個線程訪問Servlet對象,那麼它就先拿到了Servlet對象的那把鎖
20          * 等到它執行完之後纔會把鎖還給Servlet對象,由於是它先拿到了Servlet對象的那把鎖,
21          * 所以當有別的線程來訪問這個Servlet對象時,由於鎖已經被之前的線程拿走了,後面的線程只能排隊等候了
22          * 
23          */
24         synchronized (this) {//在java中,每一個對象都有一把鎖,這裏的this指的就是Servlet對象
25             i++;
26             try {
27                 Thread.sleep(1000*4);
28             } catch (InterruptedException e) {
29                 e.printStackTrace();
30             }
31             response.getWriter().write(i+"");
32         }
33         
34     }
35 
36     public void doPost(HttpServletRequest request, HttpServletResponse response)
37             throws ServletException, IOException {
38         doGet(request, response);
39     }
40 
41 }
複製代碼

  現在這種做法是給Servlet對象加了一把鎖,保證任何時候都只有一個線程在訪問該Servlet對象裏面的資源,這樣就不存在線程安全問題了,如下圖所示:

  

  這種做法雖然解決了線程安全問題,但是編寫Servlet卻萬萬不能用這種方式處理線程安全問題,假如有9999個人同時訪問這個Servlet,那麼這9999個人必須按先後順序排隊輪流訪問。

  針對Servlet的線程安全問題,Sun公司是提供有解決方案的:讓Servlet去實現一個SingleThreadModel接口,如果某個Servlet實現了SingleThreadModel接口,那麼Servlet引擎將以單線程模式來調用其service方法。
  查看Sevlet的API可以看到,SingleThreadModel接口中沒有定義任何方法和常量,在Java中,把沒有定義任何方法和常量的接口稱之爲標記接口,經常看到的一個最典型的標記接口就是"Serializable",這個接口也是沒有定義任何方法和常量的,標記接口在Java中有什麼用呢?主要作用就是給某個對象打上一個標誌,告訴JVM,這個對象可以做什麼,比如實現了"Serializable"接口的類的對象就可以被序列化,還有一個"Cloneable"接口,這個也是一個標記接口,在默認情況下,Java中的對象是不允許被克隆的,就像現實生活中的人一樣,不允許克隆,但是隻要實現了"Cloneable"接口,那麼對象就可以被克隆了。

  讓Servlet實現了SingleThreadModel接口,只要在Servlet類的定義中增加實現SingleThreadModel接口的聲明即可。  
  對於實現了SingleThreadModel接口的Servlet,Servlet引擎仍然支持對該Servlet的多線程併發訪問,其採用的方式是產生多個Servlet實例對象,併發的每個線程分別調用一個獨立的Servlet實例對象

  實現SingleThreadModel接口並不能真正解決Servlet的線程安全問題,因爲Servlet引擎會創建多個Servlet實例對象,而真正意義上解決多線程安全問題是指一個Servlet實例對象被多個線程同時調用的問題。事實上,在Servlet API 2.4中,已經將SingleThreadModel標記爲Deprecated(過時的)。  


六、ServletConfig講解

6.1、配置Servlet初始化參數

  在Servlet的配置文件web.xml中,可以使用一個或多個<init-param>標籤爲servlet配置一些初始化參數。

例如:

複製代碼
 1 <servlet>
 2     <servlet-name>ServletConfigDemo1</servlet-name>
 3     <servlet-class>gacl.servlet.study.ServletConfigDemo1</servlet-class>
 4     <!--配置ServletConfigDemo1的初始化參數 -->
 5     <init-param>
 6         <param-name>name</param-name>
 7         <param-value>gacl</param-value>
 8     </init-param>
 9      <init-param>
10         <param-name>password</param-name>
11         <param-value>123</param-value>
12     </init-param>
13     <init-param>
14         <param-name>charset</param-name>
15         <param-value>UTF-8</param-value>
16     </init-param>
17 </servlet>
複製代碼

6.2、通過ServletConfig獲取Servlet的初始化參數

  當servlet配置了初始化參數後,web容器在創建servlet實例對象時,會自動將這些初始化參數封裝到ServletConfig對象中,並在調用servlet的init方法時,將ServletConfig對象傳遞給servlet。進而,我們通過ServletConfig對象就可以得到當前servlet的初始化參數信息。

例如:

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 import java.util.Enumeration;
 5 import javax.servlet.ServletConfig;
 6 import javax.servlet.ServletException;
 7 import javax.servlet.http.HttpServlet;
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 public class ServletConfigDemo1 extends HttpServlet {
12 
13     /**
14      * 定義ServletConfig對象來接收配置的初始化參數
15      */
16     private ServletConfig config;
17     
18     /**
19      * 當servlet配置了初始化參數後,web容器在創建servlet實例對象時,
20      * 會自動將這些初始化參數封裝到ServletConfig對象中,並在調用servlet的init方法時,
21      * 將ServletConfig對象傳遞給servlet。進而,程序員通過ServletConfig對象就可以
22      * 得到當前servlet的初始化參數信息。
23      */
24     @Override
25     public void init(ServletConfig config) throws ServletException {
26         this.config = config;
27     }
28 
29     public void doGet(HttpServletRequest request, HttpServletResponse response)
30             throws ServletException, IOException {
31         //獲取在web.xml中配置的初始化參數
32         String paramVal = this.config.getInitParameter("name");//獲取指定的初始化參數
33         response.getWriter().print(paramVal);
34         
35         response.getWriter().print("<hr/>");
36         //獲取所有的初始化參數
37         Enumeration<String> e = config.getInitParameterNames();
38         while(e.hasMoreElements()){
39             String name = e.nextElement();
40             String value = config.getInitParameter(name);
41             response.getWriter().print(name + "=" + value + "<br/>");
42         }
43     }
44 
45     public void doPost(HttpServletRequest request, HttpServletResponse response)
46             throws ServletException, IOException {
47         this.doGet(request, response);
48     }
49 
50 }
複製代碼

運行結果如下:

  

七、ServletContext對象

  WEB容器在啓動時,它會爲每個WEB應用程序都創建一個對應的ServletContext對象,它代表當前web應用。
  ServletConfig對象中維護了ServletContext對象的引用,開發人員在編寫servlet時,可以通過ServletConfig.getServletContext方法獲得ServletContext對象。
  由於一個WEB應用中的所有Servlet共享同一個ServletContext對象,因此Servlet對象之間可以通過ServletContext對象來實現通訊。ServletContext對象通常也被稱之爲context域對象。

八、ServletContext的應用

  8.1、多個Servlet通過ServletContext對象實現數據共享

  範例:ServletContextDemo1和ServletContextDemo2通過ServletContext對象實現數據共享

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 import javax.servlet.ServletContext;
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 public class ServletContextDemo1 extends HttpServlet {
11 
12     public void doGet(HttpServletRequest request, HttpServletResponse response)
13             throws ServletException, IOException {
14         String data = "xdp_gacl";
15         /**
16          * ServletConfig對象中維護了ServletContext對象的引用,開發人員在編寫servlet時,
17          * 可以通過ServletConfig.getServletContext方法獲得ServletContext對象。
18          */
19         ServletContext context = this.getServletConfig().getServletContext();//獲得ServletContext對象
20         context.setAttribute("data", data);  //將data存儲到ServletContext對象中
21     }
22 
23     public void doPost(HttpServletRequest request, HttpServletResponse response)
24             throws ServletException, IOException {
25         doGet(request, response);
26     }
27 }
複製代碼
複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 import javax.servlet.ServletContext;
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 public class ServletContextDemo2 extends HttpServlet {
11 
12     public void doGet(HttpServletRequest request, HttpServletResponse response)
13             throws ServletException, IOException {
14         ServletContext context = this.getServletContext();
15         String data = (String) context.getAttribute("data");//從ServletContext對象中取出數據
16         response.getWriter().print("data="+data);
17     }
18 
19     public void doPost(HttpServletRequest request, HttpServletResponse response)
20             throws ServletException, IOException {
21         doGet(request, response);
22     }
23 }
複製代碼

  先運行ServletContextDemo1,將數據data存儲到ServletContext對象中,然後運行ServletContextDemo2就可以從ServletContext對象中取出數據了,這樣就實現了數據共享,如下圖所示:

  

  8.2、獲取WEB應用的初始化參數

  在web.xml文件中使用<context-param>標籤配置WEB應用的初始化參數,如下所示:

複製代碼
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 3     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
 4     <display-name></display-name>
 5     <!-- 配置WEB應用的初始化參數 -->
 6     <context-param>
 7         <param-name>url</param-name>
 8         <param-value>jdbc:mysql://localhost:3306/test</param-value>
 9     </context-param>
10 
11     <welcome-file-list>
12         <welcome-file>index.jsp</welcome-file>
13     </welcome-file-list>
14 </web-app>
複製代碼

  獲取Web應用的初始化參數,代碼如下:

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 import javax.servlet.ServletContext;
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 
11 public class ServletContextDemo3 extends HttpServlet {
12 
13     public void doGet(HttpServletRequest request, HttpServletResponse response)
14             throws ServletException, IOException {
15 
16         ServletContext context = this.getServletContext();
17         //獲取整個web站點的初始化參數
18         String contextInitParam = context.getInitParameter("url");
19         response.getWriter().print(contextInitParam);
20     }
21 
22     public void doPost(HttpServletRequest request, HttpServletResponse response)
23             throws ServletException, IOException {
24         doGet(request, response);
25     }
26 
27 }
複製代碼

運行結果:

  

  8.3、用servletContext實現請求轉發

ServletContextDemo4
複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 import javax.servlet.RequestDispatcher;
 6 import javax.servlet.ServletContext;
 7 import javax.servlet.ServletException;
 8 import javax.servlet.http.HttpServlet;
 9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 
12 public class ServletContextDemo4 extends HttpServlet {
13 
14     public void doGet(HttpServletRequest request, HttpServletResponse response)
15             throws ServletException, IOException {
16         String data = "<h1><font color='red'>abcdefghjkl</font></h1>";
17         response.getOutputStream().write(data.getBytes());
18         ServletContext context = this.getServletContext();//獲取ServletContext對象
19         RequestDispatcher rd = context.getRequestDispatcher("/servlet/ServletContextDemo5");//獲取請求轉發對象(RequestDispatcher)
20         rd.forward(request, response);//調用forward方法實現請求轉發
21     }
22 
23     public void doPost(HttpServletRequest request, HttpServletResponse response)
24             throws ServletException, IOException {
25     }
26 }
複製代碼
ServletContextDemo5
複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 import javax.servlet.ServletException;
 5 import javax.servlet.http.HttpServlet;
 6 import javax.servlet.http.HttpServletRequest;
 7 import javax.servlet.http.HttpServletResponse;
 8 
 9 public class ServletContextDemo5 extends HttpServlet {
10 
11     public void doGet(HttpServletRequest request, HttpServletResponse response)
12             throws ServletException, IOException {
13         response.getOutputStream().write("servletDemo5".getBytes());
14     }
15 
16     public void doPost(HttpServletRequest request, HttpServletResponse response)
17             throws ServletException, IOException {
18         this.doGet(request, response);
19     }
20 
21 }
複製代碼

  運行結果:

  

  訪問的是ServletContextDemo4,瀏覽器顯示的卻是ServletContextDemo5的內容,這就是使用ServletContext實現了請求轉發

  8.4、利用ServletContext對象讀取資源文件

  項目目錄結構如下:

   

代碼範例:使用servletContext讀取資源文件

複製代碼
  1 package gacl.servlet.study;
  2 
  3 import java.io.FileInputStream;
  4 import java.io.FileNotFoundException;
  5 import java.io.IOException;
  6 import java.io.InputStream;
  7 import java.text.MessageFormat;
  8 import java.util.Properties;
  9 import javax.servlet.ServletException;
 10 import javax.servlet.http.HttpServlet;
 11 import javax.servlet.http.HttpServletRequest;
 12 import javax.servlet.http.HttpServletResponse;
 13 
 14 /**
 15  * 使用servletContext讀取資源文件
 16  * 
 17  * @author gacl
 18  * 
 19  */
 20 public class ServletContextDemo6 extends HttpServlet {
 21 
 22     public void doGet(HttpServletRequest request, HttpServletResponse response)
 23             throws ServletException, IOException { 
 24         /**
 25          * response.setContentType("text/html;charset=UTF-8");目的是控制瀏覽器用UTF-8進行解碼;
 26          * 這樣就不會出現中文亂碼了
 27          */
 28         response.setHeader("content-type","text/html;charset=UTF-8");
 29         readSrcDirPropCfgFile(response);//讀取src目錄下的properties配置文件
 30         response.getWriter().println("<hr/>");
 31         readWebRootDirPropCfgFile(response);//讀取WebRoot目錄下的properties配置文件
 32         response.getWriter().println("<hr/>");
 33         readPropCfgFile(response);//讀取src目錄下的db.config包中的db3.properties配置文件
 34         response.getWriter().println("<hr/>");
 35         readPropCfgFile2(response);//讀取src目錄下的gacl.servlet.study包中的db4.properties配置文件
 36         
 37     }
 38 
 39     /**
 40      * 讀取src目錄下的gacl.servlet.study包中的db4.properties配置文件
 41      * @param response
 42      * @throws IOException
 43      */
 44     private void readPropCfgFile2(HttpServletResponse response)
 45             throws IOException {
 46         InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/gacl/servlet/study/db4.properties");
 47         Properties prop = new Properties();
 48         prop.load(in);
 49         String driver = prop.getProperty("driver");
 50         String url = prop.getProperty("url");
 51         String username = prop.getProperty("username");
 52         String password = prop.getProperty("password");
 53         response.getWriter().println("讀取src目錄下的gacl.servlet.study包中的db4.properties配置文件:");
 54         response.getWriter().println(
 55                 MessageFormat.format(
 56                         "driver={0},url={1},username={2},password={3}", 
 57                         driver,url, username, password));
 58     }
 59 
 60     /**
 61      * 讀取src目錄下的db.config包中的db3.properties配置文件
 62      * @param response
 63      * @throws FileNotFoundException
 64      * @throws IOException
 65      */
 66     private void readPropCfgFile(HttpServletResponse response)
 67             throws FileNotFoundException, IOException {
 68         //通過ServletContext獲取web資源的絕對路徑
 69         String path = this.getServletContext().getRealPath("/WEB-INF/classes/db/config/db3.properties");
 70         InputStream in = new FileInputStream(path);
 71         Properties prop = new Properties();
 72         prop.load(in);
 73         String driver = prop.getProperty("driver");
 74         String url = prop.getProperty("url");
 75         String username = prop.getProperty("username");
 76         String password = prop.getProperty("password");
 77         response.getWriter().println("讀取src目錄下的db.config包中的db3.properties配置文件:");
 78         response.getWriter().println(
 79                 MessageFormat.format(
 80                         "driver={0},url={1},username={2},password={3}", 
 81                         driver,url, username, password));
 82     }
 83 
 84     /**
 85      * 通過ServletContext對象讀取WebRoot目錄下的properties配置文件
 86      * @param response
 87      * @throws IOException
 88      */
 89     private void readWebRootDirPropCfgFile(HttpServletResponse response)
 90             throws IOException {
 91         /**
 92          * 通過ServletContext對象讀取WebRoot目錄下的properties配置文件
 93          * “/”代表的是項目根目錄
 94          */
 95         InputStream in = this.getServletContext().getResourceAsStream("/db2.properties");
 96         Properties prop = new Properties();
 97         prop.load(in);
 98         String driver = prop.getProperty("driver");
 99         String url = prop.getProperty("url");
100         String username = prop.getProperty("username");
101         String password = prop.getProperty("password");
102         response.getWriter().println("讀取WebRoot目錄下的db2.properties配置文件:");
103         response.getWriter().print(
104                 MessageFormat.format(
105                         "driver={0},url={1},username={2},password={3}", 
106                         driver,url, username, password));
107     }
108 
109     /**
110      * 通過ServletContext對象讀取src目錄下的properties配置文件
111      * @param response
112      * @throws IOException
113      */
114     private void readSrcDirPropCfgFile(HttpServletResponse response) throws IOException {
115         /**
116          * 通過ServletContext對象讀取src目錄下的db1.properties配置文件
117          */
118         InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db1.properties");
119         Properties prop = new Properties();
120         prop.load(in);
121         String driver = prop.getProperty("driver");
122         String url = prop.getProperty("url");
123         String username = prop.getProperty("username");
124         String password = prop.getProperty("password");
125         response.getWriter().println("讀取src目錄下的db1.properties配置文件:");
126         response.getWriter().println(
127                 MessageFormat.format(
128                         "driver={0},url={1},username={2},password={3}", 
129                         driver,url, username, password));
130     }
131 
132     public void doPost(HttpServletRequest request, HttpServletResponse response)
133             throws ServletException, IOException {
134         this.doGet(request, response);
135     }
136 
137 }
複製代碼

運行結果如下:

  

代碼範例:使用類裝載器讀取資源文件

複製代碼
  1 package gacl.servlet.study;
  2 
  3 import java.io.FileOutputStream;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.OutputStream;
  7 import java.text.MessageFormat;
  8 import java.util.Properties;
  9 
 10 import javax.servlet.ServletException;
 11 import javax.servlet.http.HttpServlet;
 12 import javax.servlet.http.HttpServletRequest;
 13 import javax.servlet.http.HttpServletResponse;
 14 
 15 /**
 16  * 用類裝載器讀取資源文件
 17  * 通過類裝載器讀取資源文件的注意事項:不適合裝載大文件,否則會導致jvm內存溢出
 18  * @author gacl
 19  *
 20  */
 21 public class ServletContextDemo7 extends HttpServlet {
 22 
 23     public void doGet(HttpServletRequest request, HttpServletResponse response)
 24             throws ServletException, IOException {
 25         /**
 26          * response.setContentType("text/html;charset=UTF-8");目的是控制瀏覽器用UTF-8進行解碼;
 27          * 這樣就不會出現中文亂碼了
 28          */
 29         response.setHeader("content-type","text/html;charset=UTF-8");
 30         test1(response);
 31         response.getWriter().println("<hr/>");
 32         test2(response);
 33         response.getWriter().println("<hr/>");
 34         //test3();
 35         test4();
 36         
 37     }
 38     
 39     /**
 40      * 讀取類路徑下的資源文件
 41      * @param response
 42      * @throws IOException
 43      */
 44     private void test1(HttpServletResponse response) throws IOException {
 45         //獲取到裝載當前類的類裝載器
 46         ClassLoader loader = ServletContextDemo7.class.getClassLoader();
 47         //用類裝載器讀取src目錄下的db1.properties配置文件
 48         InputStream in = loader.getResourceAsStream("db1.properties");
 49         Properties prop = new Properties();
 50         prop.load(in);
 51         String driver = prop.getProperty("driver");
 52         String url = prop.getProperty("url");
 53         String username = prop.getProperty("username");
 54         String password = prop.getProperty("password");
 55         response.getWriter().println("用類裝載器讀取src目錄下的db1.properties配置文件:");
 56         response.getWriter().println(
 57                 MessageFormat.format(
 58                         "driver={0},url={1},username={2},password={3}", 
 59                         driver,url, username, password));
 60     }
 61 
 62     /**
 63      * 讀取類路徑下面、包下面的資源文件
 64      * @param response
 65      * @throws IOException
 66      */
 67     private void test2(HttpServletResponse response) throws IOException {
 68         //獲取到裝載當前類的類裝載器
 69         ClassLoader loader = ServletContextDemo7.class.getClassLoader();
 70         //用類裝載器讀取src目錄下的gacl.servlet.study包中的db4.properties配置文件
 71         InputStream in = loader.getResourceAsStream("gacl/servlet/study/db4.properties");
 72         Properties prop = new Properties();
 73         prop.load(in);
 74         String driver = prop.getProperty("driver");
 75         String url = prop.getProperty("url");
 76         String username = prop.getProperty("username");
 77         String password = prop.getProperty("password");
 78         response.getWriter().println("用類裝載器讀取src目錄下的gacl.servlet.study包中的db4.properties配置文件:");
 79         response.getWriter().println(
 80                 MessageFormat.format(
 81                         "driver={0},url={1},username={2},password={3}", 
 82                         driver,url, username, password));
 83     }
 84     
 85     /**
 86      * 通過類裝載器讀取資源文件的注意事項:不適合裝載大文件,否則會導致jvm內存溢出
 87      */
 88     public void test3() {
 89         /**
 90          * 01.avi是一個150多M的文件,使用類加載器去讀取這個大文件時會導致內存溢出:
 91          * java.lang.OutOfMemoryError: Java heap space
 92          */
 93         InputStream in = ServletContextDemo7.class.getClassLoader().getResourceAsStream("01.avi");
 94         System.out.println(in);
 95     }
 96     
 97     /**
 98      * 讀取01.avi,並拷貝到e:\根目錄下
 99      * 01.avi文件太大,只能用servletContext去讀取
100      * @throws IOException
101      */
102     public void test4() throws IOException {
103         // path=G:\Java學習視頻\JavaWeb學習視頻\JavaWeb\day05視頻\01.avi
104         // path=01.avi
105         String path = this.getServletContext().getRealPath("/WEB-INF/classes/01.avi");
106         /**
107          * path.lastIndexOf("\\") + 1是一個非常絕妙的寫法
108          */
109         String filename = path.substring(path.lastIndexOf("\\") + 1);//獲取文件名
110         InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/01.avi");
111         byte buffer[] = new byte[1024];
112         int len = 0;
113         OutputStream out = new FileOutputStream("e:\\" + filename);
114         while ((len = in.read(buffer)) > 0) {
115             out.write(buffer, 0, len);
116         }
117         out.close();
118         in.close();
119     }
120 
121     public void doPost(HttpServletRequest request, HttpServletResponse response)
122             throws ServletException, IOException {
123 
124         this.doGet(request, response);
125     }
126 
127 }
複製代碼

  運行結果如下:

  

九、在客戶端緩存Servlet的輸出

  對於不經常變化的數據,在servlet中可以爲其設置合理的緩存時間值,以避免瀏覽器頻繁向服務器發送請求,提升服務器的性能。例如:

複製代碼
 1 package gacl.servlet.study;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 public class ServletDemo5 extends HttpServlet {
11 
12     public void doGet(HttpServletRequest request, HttpServletResponse response)
13             throws ServletException, IOException {
14         String data = "abcddfwerwesfasfsadf";
15         /**
16          * 設置數據合理的緩存時間值,以避免瀏覽器頻繁向服務器發送請求,提升服務器的性能
17          * 這裏是將數據的緩存時間設置爲1天
18          */
19         response.setDateHeader("expires",System.currentTimeMillis() + 24 * 3600 * 1000);
20         response.getOutputStream().write(data.getBytes());
21     }
22 
23     public void doPost(HttpServletRequest request, HttpServletResponse response)
24             throws ServletException, IOException {
25 
26         this.doGet(request, response);
27     }
28 
29 }
複製代碼


發佈了48 篇原創文章 · 獲贊 5 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章