總結:php
記住:servlet對象在應用程序運行的過程當中只建立一次,瀏覽器每次訪問的時候,建立reponse對象 request對象,而後調用servlet的service方法,reponse對象和request對象做爲service函數的參數傳遞進行,在service方法中再依據客戶端的get或者post請求,html
在調用doGet或者post的方法;java
servlet的聲明週期是 init() service() 和destroy方法。init在servlet被tomcat建立的時候調用,在調用init方法的時候,tomcat會建立一個ServletConfig對象,在init方法中能夠得到該對象來得到web.xml配置的一些初始化的參數。mysql
service在每次客戶端請求的時候調用,當servlet對象已經存在的時候,收到了客戶端的請求不會再建立servlet,收到客戶端請求的時候會調用service方法,當應用進程退出的時候會調用servlet的destroy方法。程序員
由於tomcat對於客戶端的每次請求,多個線程都共享一個servlet對象,若是在servlet中定義了全局的變量,多個線程若是共享一個全局變量,就會存在線程安全,servlet必定要注意線程安全問題。web
結合右邊給出的流程圖:sql
當客戶端瀏覽器向服務器請求一個 Servlet 時,服務器收到該請求後,首先到容器中檢索與請求匹配的 Servlet 實例是否已經存在。數據庫
--若不存在,則 Servlet 容器負責加載並實例化出該類 Servlet的一個實例對象,接着容器框架負責調用該實例的 init() 方法來對實例作一些初始化工做,而後Servlet 容器運行該實例的 service() 方法。apache
--若 Servlet 實例已經存在,則容器框架直接調用該實例的 service() 方法。
service() 方法在運行時,自動派遣運行與用戶請求相對應的 doXX() 方法來響應用戶發起的請求。瀏覽器
一般,每一個 Servlet 類在容器中只存在一個實例,每當請求到來時,則分配一條線程來處理該請求。
在處理請求時:
一、Servlet容器會建立一個請求對象ServletRequst,其中封裝了用戶請求的信息,以便處理客戶端請求,此外還會建立一個響應對象ServletResponse,用於響應客戶端請求,想客戶端返回數據。
二、而後Servlet容器把建立好的ServletRequst和ServletResponse對象傳給用戶所請求的Servlet。
三、Servlet利用ServletResponse包含的數據和自身的業務邏輯處理請求,並把處理好的結果寫在ServletResponse中,最後Servlet容器把響應結果傳給用戶。
一、自定義Servlet通常須要繼承HttpServlet,而HttpServlet是繼承GenericServlet,而GenericServlet是繼承Servlet。
緣由:HttpServlet是特定於HTTP協議的類,Servlet接口和GenericServlet是不特定於任何協議的。Servlet接口中定義了五個方法,其中比較重要的三個方法涉及到Servlet的生命週期,分別是上文提到的init(),service(),destroy()方法。GenericServlet是一個通用的,不特定於任何協議的Servlet,它實現了Servlet接口。而HttpServlet繼承於GenericServlet,所以HttpServlet也實現了Servlet接口。因此咱們定義Servlet的時候只須要繼承HttpServlet便可。在HttpServlet中實現了service()方法,並將請求ServletRequest,ServletResponse強轉爲HttpRequest和HttpResponse。
二、請求的實際處理者是doGet,doPost方法,自定義Servlet類須要重寫doGet,doPost方法。
基類HttpServlet中的doGet,doPost方法並無具體實現,只給出一些異常處理,返回的是錯誤信息。因此具體的處理代碼咱們須要本身寫。
三、什麼操做須要在doGet方法處理,什麼操做須要在doPut方法處理?
Get方式的請求:直接在瀏覽器地址欄輸入訪問的地址所發送的請求或表單發送時沒有指明post形式發送的(表單默認爲get提交)都會調用doGet方法。
分析:
1)因爲get方式請求會將請求參數的名和值轉換成字符串,並附在原URl以後,所以能夠在地址欄上看見請求參數的名和值,安全性比較差。
2)get請求的數據量比較小。
3)只能傳遞字符串,不能傳遞二進制數據
4)服務器隨機接受GET方法的數據,一旦斷電等緣由,服務器也不知道信息是否發送完畢
Post方式的請求:表單以post方式發送。
分析:
1)POST方式發送的請求參數以及對應的值放在html header中傳輸,安全性相對較高。
2)post傳遞的數據量比較大,一般認爲請求參數大小不受限制,但每每取決於服務器端的限制。
3)傳遞數據的類型沒有限制,能夠傳遞二進制數據
4)並且此外,Post方法接受數據時,服務器先接受數據信息的長度,而後再接受數據,使用post方式,服務器能夠知道數據是否完整。
對比:圖來自HTTP 方法:GET 對比 POST
經常使用的使用方法:
一般都使用dopost方法,以後再doGet方法調用doPost方法便可
這裏給出Stack Overflow中關於Servlets: doGet and doPost的解釋,以及其給出的實例代碼
圖3。。。
http://blog.csdn.net/insistgogo/article/details/20788749
看博客:http://blog.csdn.net/mhx5201314/article/details/50932532
ServletContext對象表示的當前web進程對象,可使用多個servlet之間數據的共享,能夠用來讀取系統配置文件,至關的經典。
1、Servlet簡介
Servlet是sun公司提供的一門用於開發動態web資源的技術。
Sun公司在其API中提供了一個servlet接口,用戶若想用發一個動態web資源(即開發一個Java程序向瀏覽器輸出數據),須要完成如下2個步驟:
一、編寫一個Java類,實現servlet接口。
二、把開發好的Java類部署到web服務器中。
按照一種約定俗成的稱呼習慣,一般咱們也把實現了servlet接口的java程序,稱之爲Servlet
2、Servlet的運行過程
Servlet程序是由WEB服務器調用,web服務器收到客戶端的Servlet訪問請求後:
①Web服務器首先檢查是否已經裝載並建立了該Servlet的實例對象。若是是,則直接執行第④步,不然,執行第②步。
②裝載並建立該Servlet的一個實例對象。
③調用Servlet實例對象的init()方法。
④建立一個用於封裝HTTP請求消息的HttpServletRequest對象和一個表明HTTP響應消息的HttpServletResponse對象,而後調用Servlet的service()方法並將請求和響應對象做爲參數傳遞進去。
⑤WEB應用程序被中止或從新啓動以前,Servlet引擎將卸載Servlet,並在卸載以前調用Servlet的destroy()方法。
在eclipse中新建一個web project工程,eclipse會自動建立下圖所示目錄結構:
Servlet接口SUN公司定義了兩個默認實現類,分別爲:GenericServlet、HttpServlet。
HttpServlet指可以處理HTTP請求的servlet,它在原有Servlet接口上添加了一些與HTTP協議處理方法,它比Servlet接口的功能更爲強大。所以開發人員在編寫Servlet時,一般應繼承這個類,而避免直接去實現Servlet接口。
HttpServlet在實現Servlet接口時,覆寫了service方法,該方法體內的代碼會自動判斷用戶的請求方式,如爲GET請求,則調用HttpServlet的doGet方法,如爲Post請求,則調用doPost方法。所以,開發人員在編寫Servlet時,一般只須要覆寫doGet或doPost方法,而不要去覆寫service方法。
選中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,以下圖所示:
因爲客戶端是經過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> 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的註冊名。 例如:
<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上。
在Servlet映射到的URL中也可使用*通配符,可是隻能有兩種固定的格式:一種格式是"*.擴展名",另外一種格式是以正斜槓(/)開頭並以"/*"結尾。例如:
<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。
匹配的原則就是"誰長得更像就找誰"
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應用建立必要的數據庫表和數據。
若是某個Servlet的映射路徑僅僅爲一個正斜槓(/),那麼這個Servlet就成爲當前Web應用程序的缺省Servlet。
凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它們的訪問請求都將交給缺省Servlet處理,也就是說,缺省Servlet用於處理全部其餘Servlet都不處理的訪問請求。 例如:
<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。
當多個客戶端併發訪問同一個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(過期的)。
在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>
當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 }
運行結果以下:
WEB容器在啓動時,它會爲每一個WEB應用程序都建立一個對應的ServletContext對象,它表明當前web應用。
ServletConfig對象中維護了ServletContext對象的引用,開發人員在編寫servlet時,能夠經過ServletConfig.getServletContext方法得到ServletContext對象。
因爲一個WEB應用中的全部Servlet共享同一個ServletContext對象,所以Servlet對象之間能夠經過ServletContext對象來實現通信。ServletContext對象一般也被稱之爲context域對象。
範例: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對象中取出數據了,這樣就實現了數據共享,以下圖所示:
在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 }
運行結果:
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實現了請求轉發
項目目錄結構以下:
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中能夠爲其設置合理的緩存時間值,以免瀏覽器頻繁向服務器發送請求,提高服務器的性能。例如:
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 }