(JavaEE-04)Servlet

#Servlethtml

  • Servlet是sun公司提供的一套專門用於開發動態web資源的技術
  • sun公司在ServletAPI中提供了一個servlet接口,若是需咱們須要開發一個動態web資源,須要兩步:
  • 編寫一個Java類,實現servlet接口
  • 把開發好的Java類部署到web服務器中

###demo:使用servlet給瀏覽器輸出「Hello World!~」java

  • 首先查看ServletAPI,Servlet開發屬於JavaEE技術,在JavaSEAPI中是沒有的,須要查看JavaEEAPI,可是JavaEEAPI太過繁多,建議直接查看ServletAPI
  • 根據API能夠得知Servlet具體釋義以及相關接口和類(GenericServlet, HttpServlet)
  • 在webapps下新建一個web應用,搭建必要的目錄結構與文件(WEB-INF、classes、lib、web.xml)
  • 在classes文件夾下新建一個Java源文件(也能夠不在這裏),文件名叫HelloServlet.java(能夠不是這個名字)
  • 繼承GenericServlet接口,重寫service方法,在service方法中使用response對象編寫給客戶端發送數據的程序(獲取輸出流)
  • 添加import和package語句
  • 對該文件進行編譯,因爲有package,要使用 -d 來進行編譯,並且須要使用JavaEE的類,因此須要JavaEE的Jar文件
  • 使用set classpath來設置類文件路徑(set classpath=%classpath%;serlvet-api.jar文件的目錄)
  • javac -d . HelloServlet.java(進行編譯)
  • 將這個Servlet配置到web容器中(配置web.xml,添加servlet元素與servlet-mapping元素)
  • 啓動服務器,訪問Servlet

使用UML描述Servlet的調用過程web

###Servlet的生命週期 Servlet會在第一次被訪問的時候建立出相應的Servlet對象,爲了方便後續請求的訪問,Web容器建立一個Servlet對象後,會一直將其保存在容器中,之後關於該Servlet的請求,都使用該對象處理,一直不會被銷燬,直到web容器關閉。也就是說,在一個Servlet運行的過程當中,web容器中有且只有一個該Servlet對象存在,多個請求使用多線程來分別處理,一個請求對應一個線程,多個請求多個線程(Tomcat會給每一個線程建立對應的request對象和response對象,一塊兒交給Servlet對象),這些線程共享Servlet對象!能夠得出一個結論:Servlet是非線程安全的,可是request對象和response對象是線程安全的數據庫

Servlet的生命週期演示(init方法與destroy方法)設計模式

###使用Eclipse開發Servletapi

  • 怎樣建立一個web項目
  • 給Eclipse配置Tomcat服務器(Tomcat使用的虛擬機與web應用編譯器的版本問題)
  • 導入web開發的相關jar文件
  • Eclipse幫助咱們都幹了哪些事情

###HttpServlet HttpServlet指得是可以處理HTTP請求的serlvet,它在原有的Servlet接口上添加了一些HTTP協議處理的方法,它更增強大,在開發中一般繼承這個類。 HttpServlet在實現Servlet接口時,重寫了service方法,該方法內部會自動判斷用戶的請求方式,好比是get請求,則會調用HttpServlet的doGet方法,post請求,調用doPost方法。 在開發中,直接覆蓋doGet和doPost方法,不用覆蓋service方法。瀏覽器

閱讀HttpServlet文檔,查看HttpServlet源碼(ctrl+shift+T) 使用Eclipse建立一個新的Servlet,繼承HttpServlet,直接建立Servlet,自動生成配置(一個Servlet能夠配置多個映射)緩存

#Servlet細節總結安全

  1. **Servlet若是想要外部訪問,必須把Servlet程序映射到一個URL地址上,在web.xml中使用`<servlet>`元素和`<servlet-mapping>`元素完成**
     >`<servlet>`元素用於註冊Servlet,它包含兩個子元素:`<servlet-name>`和`<servlet-class>`,分別用於註冊Servlet的註冊名稱和Servlet的完整類名
     >`<servlet-mapping>`元素用於映射一個已經註冊的Servet對外訪問路徑,它包含兩個子元素:`<servlet-name>`和`<url-pattern>`,分別用於指定註冊名和對外訪問路徑
  2. 用一個Servlet能夠被映射到多個URL上服務器

    在Servlet映射到的URL,可使用通配符來配置,可是隻能有兩種固定的格式: > * 「 * . 擴展名」,如: *.do,*.html(僞靜態) > * "/* ",如:/action/*

  3. 使用了通配符後,就會產生一些新的問題,以下:

    • Servlet1 映射到 /abc/*
    • Servlet2 映射到 /*
    • Servlet3 映射到 /abc
    • Servlet4 映射到 *.do
那麼當這樣通配後,對於類似的URL請求會怎麼去處理呢?舉例來講明:

* `/abc/a.html,/abc/* 和 /* 都匹配,Servlet引擎將會調用Servlet1`
* `/abc,/abc/* 和 /abc 都匹配,Servlet引擎將會調用Servlet3`
* `/abc/a.do,/abc/* 和 *.do 都匹配,Serlvet引擎將會調用Servlet1`
* `/a.do,/* 和 *.do 都匹配,Servlet引擎將會調用Servlet2`
* `/xxx/yyy/a.do,/* 和 *.do 都匹配,Servlet引擎將會調用Servlet2`

\*號開頭的優先級最低!不以\*開頭的話,哪一個最像選擇哪一個
  1. Servlet是不能獨立運行的,它的運行徹底是由Servlet引擎來控制和調度的。一個Servlet若是被訪問,無論訪問多少次,Web容器中以後一個Servlet對象,被多個請求(線程)共享,可是每次執行service方法,都是根據此次請求從新建立的請求對象和響應對象。當本次請求完成,請求對象和響應對象都會被銷燬(響應對象的銷燬在建立了標準的Http響應以後)。

  2. <serlvet>元素能夠配置隨着web容器的啓動而建立該Servlet對象。使用<load-on-startup>元素來完成,在建立過程當中,就會調用Servlet的init方法。若是有一些操做須要在服務 器啓動的時候就完成,就可使用這種方式來完成(初始化數據庫鏈接池等)。

  3. 若是某個Servlet的映射路徑爲 "/",那麼這個Servlet就是當前web應用的默認Servlet。凡是在web.xml中找不到映射的URL,它們的訪問請求都將交給這個默認的Servlet處理,也就是說,默認的Servlet用於處理全部其它Servlet都不處理的請求。

    其實對於Tomcat來講,咱們全部的請求都不能直接到達web資源,都會通過一個Servlet。如:http://localhost:8080/testweb/1.html ,若是項目根目錄有一個 1.html 文件 是能夠請求的到的,但其實,並非直接訪問了該資源,Tomcat有一個默認的Servlet,由這個默認的Servlet讀取了這個資源,而後返回給客戶端。若是沒有,就是404 若是你自定義了默認的Servlet,將會覆蓋掉系統的默認Servlet,因此不建議這麼作。(查看Tomcat的web.xml,其實全部的靜態資源,都由該Servlet處理)

  4. 線程安全

    Servlet存在線程安全問題,在實際開發中,要根據具體狀況來編寫解決同步的代碼 在Servlet中編寫程序時,要注意對象的靜態屬性的處理,否則會引起內容溢出的問題(對象的靜態集合屬性處理) 標準解決方案:同步代碼塊。非標準解決方案:SingleThreadModel(已經被廢棄) 對於Servlet的線程安全問題,的確是一個比較靈活的問題,那麼有如下幾條開發建議,能夠避免Servlet的線程安全問題: 1. 儘可能避免使用成員變量,若是萬不得已使用了,就須要同步,可是注意同步可用性最小的代碼路徑 2. 要清楚request是線程安全的,HttpSession,ServletContext都不會線程安全的 3. 使用同步的集合類 4. 不要在Servlet中建立本身的線程來完成某個功能(增長了複雜度) 5. 在多個servlet中對外部對象(好比文件)進行修改操做時,必定要加鎖,作到互斥的訪問效果

#ServletConfig對象

  • 在Servlet的配置文件中,可使用一個或多個<init-param>標籤爲servlet配置一些初始化參數
  • 當配置了初始化參數後,在web容器在建立servlet實例對象時,會自動將這些初始化參數封裝到ServletConfig對象中,並在調用servlet的init方法時,將ServletConfig對象傳遞 servlet。進而,在程序中經過ServletConfig對象就能夠獲得當前servlet的初始化參數信息。
  • ServletConfig通常應用於幫助Servlet存儲一些固定信息,如:字符編碼、數據庫鏈接、獲取配置文件等
  • 若是使用的Servlet繼承了HttpServlet,因爲在GenericServlet中實現了對於Config對象的處理,因此在本身的Servlet中,直接使用getConfig對象就能夠了。

#ServletContext對象(重點)

  • Web容器在啓動時,它會爲每一個Web應用都建立一個對應的ServletContext對象,它表明當前web應用。
  • 經過ServletConfig對象是能夠獲取ServletContext對象的(若是繼承了HttpServlet,能夠直接使用getServletContext()來獲取ServletContext對象)
  • 一個Web應用只有一個ServletContext,因此這個對象將會被全部的Servlet所共享,經過這個特性,咱們能夠利用ServletContext讓多個Servlet進行數據傳遞
  • 要明白Servlet對象是由web服務器來掌管生死,千萬不要本身建立Servlet對象來進行傳值
  • ServletContext對象一般被稱之爲Context域對象
  • 查看ServletContext API文檔
  • ServletContext初始化參數
  • 獲取Servlet規範的版本號,獲取文件的MIME類型表示
  • ServletContext能夠作請求轉發,將一個Servlet的請求轉發給另外一個,通常用於(MVC設計模式)

###MVC簡單介紹 到目前爲止,對於客戶端的請求,咱們都是使用對應的Servlet來作簡單處理,可是問題是,通常的,不可能就簡單在返回一個字符串或者在控制檯打印一個語句,應該給客戶返回一個html頁面,那麼這個html頁面哪裏來?若是這個html是個靜態資源,那麼很簡單,可是咱們開發的是動態web資源,該怎麼辦呢?其實很簡單,使用Servlet的response對象發送html的字符流給客戶端。這種方法表面上看起來是沒有什麼問題,可是實際操做你就會發現,這個作法至關麻煩,並且開發效率很低,應該專門有一種特殊能力的Servlet來處理html的繪製,這種具備特殊能力的Servlet就是JSP。

MVC簡單介紹

JSP其實就是一個特殊的Servlet,它能很好的處理html的問題,使得頁面的渲染與程序邏輯的處理能夠得以分離。(demo,在Servlet中傳值並轉發給一個JSP)

#在Servlet中獲取各類資源文件(重點)

在實際開發中,不少時候須要獲取服務器上的一些別的資源來幫助開發,這些資源的獲取方式都不太同樣。

從web目錄中讀取資源文件

若是在開發中,須要的文件存儲在WebContent(項目目錄)中,那就使用ServletContext來讀取,相關的方法有:

  • getRealPath(String path),返回一個虛擬路徑對應資源的真實路徑
  • getResource(String path),返回一個虛擬路徑對應資源的URL對象
  • getRescourceAsStream(String path),返回一個虛擬路徑對應資源的輸入流
  • getResoucePaths(String path),返回一個虛擬路徑下的全部對應資源的集合

這些方法中的參數都不該該是資源的絕對路徑或相對路徑,應該是一個針對當前應用的虛擬路徑,應該以 「/」 開頭,這個 "/" 就表示web應用所在目錄,也成爲項目根目錄,而後按照資源在應用的目錄結構來指定資源的虛擬路徑。

從源代碼src(類路徑)文件夾中讀取資源文件

在不少時候,咱們的資源文件並不在Web資源目錄中,而是在類目錄中,這個時候怎麼讀取呢?

  • 使用類加載器來獲取類路徑中的資源:getSystemResource(String path),getResource(String path)
  • 使用類自己來獲取類路徑中的資源:getResource(String path)
  • 使用ServletContext獲取項目根目錄,而後按照項目層級去讀取文件(WEB-INF/classes/a.txt)
  • 根據類加載器來獲取資源時,不要添加 "/",類加載器會從該加載器的類路徑根目錄來查找 path 指定的資源,加了 "/",反而找不到。
  • 使用類自己來獲取資源時,加 "/" ,意味着從類路徑的根目錄來查找,不加 "/",意味着,從當前類所在的文件夾來查找資源
  • web應用和本地方法運行的效果是不同的,有關於System的資源獲取,在web應用中是不起做用的
  • 還有 getResourceAsStream(String path)系列的方法,跟上述的 getResource(String path)方法特色是一致的,只不過一個是返回URL對象,一個是返回流對象
  • 使用類加載器的 getResourceAsStream(String path) 方法來獲取資源的輸入流時,不能實現動態讀取文件變動,應該用類加載器來獲取路徑,而後用傳統的讀取文件的方式再次讀取文件,這樣就能實現實時更新數據了
  • 若是資源文件過大,不要使用類加載器的方式來直接獲取,由於這種方式會直接把資源所有加載進內存,容易內存溢出,使用類加載器獲取地址,用傳統方式的流而後緩衝讀取
  • 關於System與no-System的方法有什麼區別,我已經總結好了,若是須要進一步瞭解,參看官方關於Resources的解釋

#在客戶端緩存Servlet的輸出 對於不常常變化的數據,在Servlet中能夠爲其設置合理的緩存時間值,以免瀏覽器頻繁得向服務器發送請求,提高服務器的性能。

String data = "aaaaaaaaaaaaaaaaaaaaaa";
long time = System.currentTimeMillis() + 1 * 24 * 60 * 60 * 1000;
System.out.println("hehe");
response.setDateHeader("expires", time);
response.getWriter().write(data);
相關文章
相關標籤/搜索