一、Servlet概述
1.sun公司提供的動態web資源開發技術。本質是上一段java小程序,要求這個小程序必須實現Servlet接口,以便服務器能夠調用。
2.開發Servlet的兩個步驟*實驗:
Servlet的快速入門
(1)步驟一:寫一個java程序實現Servlet接口(此處直接繼承了默認實現類GenericServlet)
package cn.itheima; import java.io.*;
import javax.servlet.*;
public class FirstServlet extends GenericServlet{
????public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{ ????????res.getOutputStream().write("My FirstServlet!".getBytes());
????}
}
(2)將編譯好的帶包的.class放到WEB-INF/classes下以外,還要配置web應用的 web.xml注冊
二、Servlet的詳述
1.生命周期:一件事物什么時候生,什么時候死,在生存期間必然會做的事情,所有這些放在一起就是該事物的聲明周期。
2.Servlet的生命周期:通常情況下,servlet第一次被訪問的時候在內存中創建對象,在創建后立即調用init()方法進行初始化。對于每一次請求都掉用service(req,resp)方法處理請求,此時會用Request對象封裝請求信息,并用Response對象(最初是空的)代表響應消息,傳入到service方法里供使用。當service方法處理完成后,返回服務器服務器根據Response中的信息組織稱響應消息返回給瀏覽器。響應結束后servlet并不銷毀,一直駐留在內存中等待下一次請求。直到服務器關閉或web應用被移除出虛擬主機,servlet對象銷毀并在銷毀前調用destroy()方法做一些善后的事情。
3.Servlet接口的繼承結構
Servlet接口:定義了一個servlet應該具有的方法,所有的Servlet都應該直接或間接實現此接口
????|
????|----GenericServlet:對Servlet接口的默認實現,通用Servlet,這是一個抽象類,其中的大部分方法都做了默認實現,只有service方法是一抽????象方法需要繼承者自己實現
????????????????????|
? ? ? ? ? ? ? ? ? ? |----HttpServlet:對HTTP協議進行了優化的Servlet,繼承自GenericServlet類,并且實現了其中的service抽象方法,默認的實現中判斷了請求的請求方式,并根據請求方式的不同分別調用不同的doXXX()方法。通常我們直接繼承HttpServlet即可
4.6servlet的線程安全問題
4.6.1由于通常情況下,一個servlet在內存只有一個實例處理請求,當多個請求發送過來的時候就會有多個線程操作該servlet對象,此時可能導致線程安全問題。
(1)serlvet的成員變量可能存在線程安全問題
*實驗:定義一個成員變量 int i = 0;在doXXX()方法中進行i++操作并輸出i值到客戶端,此時由于延遲可能導致線程安全問題
(2)serlvet操作資源文件時,多個線程操作同一文件引發線程安全問題
*實驗:請求帶著一個參數過來,servlet將請求參數寫入到一個文件,再讀取該文件,將讀取到的值打印到客戶端上,有可能有線程安全問題
4.6.2解決方法
(1)利用同步代碼塊解決問題。缺陷是,同一時間同步代碼塊只能處理一個 請求,效率很低下,所以同步代碼塊中盡量只包含核心的導致線程安全問題的代碼。
(2)為該servlet實現SingleThreadModel接口,此為一個標記接口,被標記的servlet將會在內存中保存一個servlet池,如果一個線程來了而池中沒有servlet對象處理,則創建一個新的。如果池中有空閑的servlet則直接使用。這并不能真的解決線程安全問題。此接口已經被廢棄。
(3)兩種解決方案都不夠完美,所以盡量不要在servlet中出現成員變量。
三、ServletConfig
????????????1.代表servlet配置的對象,可以在web.xml中<servlet>中配置
?? ? ? <servlet>
????????????<servlet-name>Demo5Servlet</servlet-name>
<servlet-class>cn.it.Demo5SErvlet</servlet-class>
? ? <init-param>
? ? ? ? <param-name>data1</param-name>
? ? ? ? <param-value>value1</param-value>
? ? </init-param>
</servlet>
然后在servlet中利用this.getServletConfig()獲取ServletConfig對象,該對象提供了getInitParameter()和getInitParameterNames()方法,可以遍歷出配置中的配置項。 不想在servlet中寫死的內容可以配置到此處。
四、ServletContext
1.代表當前web應用的對象。
2.作為域對象使用,在不同servlet之間傳遞數據,作用范圍是整個web應用生命周期:當web應用被加載進容器時創建代表整個web應用的ServletContext對象。
當服務器關閉或web應用被移除出容器時,ServletContext對象跟著銷毀。~域:一個域就理解為一個框,這里面可以放置數據,一個域既然稱作域,他就有一個可以被看見的范圍,這個范圍內都可以對這個域中的數據進行操作,那這樣的對象就叫做域對象。
3.在web.xml可以配置整個web應用的初始化參數,利用ServletContext去獲得param1pvalue1this.getServletContext().getInitParameter("param1")this.getServletContext().getInitParameterNames()
4.在不同servlet之間進行轉發this.getServletContext().getRequestDispatcher("/servlet/Demo10Servlet").forward(request, response);方法執行結束,service就會返回到服務器,再有服務器去調用目標servlet,其中request會重新創建,并將之前的request的數據拷貝進去。
5.讀取資源文件5.1由于相對路徑默認相對的是java虛擬機啟動的目錄,所以我們直接寫相對路徑將會是相對于tomcat/bin目錄,所以是拿不到資源的。如果寫成絕對路徑,當項目發布到其他環境時,絕對路徑就錯了。
5.2為了解決這個問題ServletContext提供了this.getServletContext().getRealPath("/1.properties"),給進一個資源的虛擬路徑,將會返回該資源在當前環境下的真實路徑。this.getServletContext().getResourceAsStream("/1.properties"),給一個資源的虛擬路徑返回到該資源真實路徑的流。5.3當在非servlet下獲取資源文件時,就沒有ServletContext對象用了,此時只能用類加載器classLoader.getResourceAsStream("../../1.properties"),此方法利用類加載器直接將資源加載到內存中,有更新延遲的問題,以及如果文件太大,占用內存過大。classLoader.getResource("../1.properties").getPath(),直接返回資源的真實路徑,沒有更新延遲的問題。