JavaWeb--深入Servlet與JSP(運行原理)


JavaWeb--深入Servlet與JSP(運行原理)

復習復習!!!搞起來!!Servlet和JSP是Java EE規范最基本成員,他們是Java Web開發的重點知識,即使我們經常使用框架開發后端,但是我們還是很必要去理解他們的原理的。

文章結構:(1)剖析Servlet;(2)剖析JSP;


一、剖析Servlet:

(1)概述:

Servlet是一種獨立於平台和協議的服務器端的Java應用程序,可以生成動態的web頁面。它擔當Web瀏覽器或其他http客戶程序發出請求、與http服務器上的數據庫或應用程序之間交互的中間層。

Servlet是用Java編寫的Server端程序,它與協議和平台無關。Servlet運行於Java服務器中。
Java Servlet可以動態地擴展服務器的能力,並采用請求-響應模式提供Web服務。
Servlet是使用Java Servlet應用程序設計接口及相關類和方法的Java程序。它在Web服務器上或應用服務器上運行並擴展了該服務器的能力。Servlet裝入Web服務器並在Web服務器內執行。
Servlet是以Java技術為基礎的服務器端應用程序組件,Servlet的客戶端可以提出請求並獲得該請求的響應,它可以是任何Java程序、瀏覽器或任何設備。

(2)基本知識:

1.配置:

編輯好的servlet源文件並不能響應用戶請求,還必須將其編譯成class文件,將編譯好的class文件放到WEB-INF/classes路徑下,如果servlet有包,則還需要將class文件放到包路徑下。

2.生命周期:

這里寫圖片描述

編寫的JSP頁面最終將由web容器編譯成對應的servlet,當servlet在容器中運行時,其實例的創建及銷毀等都不是有程序猿決定的,而是由web容器進行控制的。

servlet容器負責加載和實例化Servlet,在容器啟動時根據設置決定是在啟動時初始化(loadOnStartup大於等於0在容器啟動時進行初始化,值越小優先級越高),還是延遲初始化直到第一次請求前;

初始化:

init(),執行一些一次性的動作,可以通過ServletConfig配置對象,獲取初始化參數,訪問ServletContext上下文環境;

請求處理:

servlet容器封裝Request和Response對象傳給對應的servlet的service方法,對於HttpServlet,就是HttpServletRequest和HttpServletResponse; HttpServlet中使用模板方法模式,service方法根據HTTP請求方法進一步分派到doGet,doPost等不同的方法來進行處理;
對於HTTP請求的處理,只有重寫了支持HTTP方法的對應HTTP servlet方法(doGet),才可以支持,否則放回405(Method Not Allowed)。

3.訪問servlet的配置參數:

配置servlet時,還可以增加額外的配置參數,通過使用配置參數,可以實現提供更好的可移植性,避免將參數以編碼方式寫在程序代碼中。

配置參數有兩種方式:
(1)通過@WebServlet的initParams屬性來指定。
(2)通過在web.xml文件的

4.Servlet的數量:

Servlet默認是線程不安全的,一個容器中只有每個servlet一個實例。

StandardWrapper源碼中寫明,這個類負責Servlet的創建,其中SingleThreadModule模式下創建的實例數不能超過20個,也就是同時只能支持20個線程訪問這個Serlvet,因此,這種對象池的設計會進一步限制並發能力和可伸縮性。

5.缺點:

開發效率低、 程序可移植性差、 程序可維護性差

6.標准mvc模式中的servlet:

僅作為控制器使用,JavaEE應用架構正是遵循mvc模式的,其中JSP僅作為表現層技術,其作用有兩點:1.負責收集用戶請求參數;2. 將應用的處理結果、狀態、數據呈現給用戶。

7.線程不安全 :

servlet中默認線程不安全,單例多線程,因此對於共享的數據(靜態變量,堆中的對象實例等)自己維護進行同步控制,不要在service方法或doGet等由service分派出去的方法,直接使用synchronized方法,很顯然要根據業務控制同步控制塊的大小進行細粒度的控制,將不影響線程安全的耗時操作移出同步控制塊;

Servlet多線程機制背后有一個線程池在支持,線程池在初始化初期就創建了一定數量的線程對象,通過提高對這些對象的利用率,避免高頻率地創建對象,從而達到提高程序的效率的目的。(由線程來執行Servlet的service方法,servlet在Tomcat中是以單例模式存在的, Servlet的線程安全問題只有在大量的並發訪問時才會顯現出來,並且很難發現,因此在編寫Servlet程序時要特別注意。線程安全問題主要是由實例變量造成的,因此在Servlet中應避免使用實例變量。如果應用程設計無法避免使用實例變量,那么使用同步來保護要使用的實例變量,但為保證系統的最佳性能,應該同步可用性最小的代碼路徑)

8.異步處理:

在Servlet中等待是一個低效的操作,因為這是阻塞操作。

異步處理請求能力,使線程可以返回到容器,從而執行更多的任務。當開始異步處理請求時,另一個線程或回調可以:(1)產生響應;或者,(2)請求分派;或者,(3)調用完成;

關鍵方法:

啟用:讓servlet支持異步支持:asyncSupported=true;
啟動AsyncContextasyncContext=req.startAsyncContext();或startAsyncContext(req,resp);
完成:asyncContext.complete();必須在startAsync調用之后,分派進行之前調用;同一個AsyncContext不能同時調用dispatch和complete
分派:asyncContext.dispatch();dispatch(Stringpath);dispatch(ServletContextcontext,Stringpath); 不能在complete之后調用; 從一個同步servlet分派到異步servlet是非法的;
超時:
asyncContext.setTimeout(millis); 超時之后,將不能通過asyncContext進行操作,但是可以執行其他耗時操作;
在異步周期開始后,容器啟動的分派已經返回后,調用該方法拋出IllegalStateException;如果設置成0或小於0就表示notimeout; 超時表示HTTP連接已經結束,HTTP已經關閉,請求已經結束了。

啟動新線程 :

通過AsyncCOntext.start(Runnable)方法,向線程池提交一個任務,其中可以使用AsyncContext(未超時前);
事件監聽:addListener(newAsyncListener{…});
onComplete:完成時回調,如果進行了分派,onComplete方法將延遲到分派返回容器后進行調用;
onError:可以通過AsyncEvent.getThrowable獲取異常;
onTimeout:超時進行回調;
onStartAsync:在該AsyncContext中啟動一個新的異步周期(調用startAsyncContext)時,進行回調;

超時和異常處理,步驟:

(1)調用所有注冊的AsyncListener實例的onTimeout/onError;
(2)如果沒有任何AsyncListener調用AsyncContext.complete()或AsyncContext.dispatch(),執行一個狀態碼為HttpServletResponse .SC_INTERNAL_SERVER_ERROR出錯分派;
(3)如果沒有找到錯誤頁面或者錯誤頁面沒有調用AsyncContext.complete()/dispatch(),容器要調用complete方法;

servlet生命終止:

servlet容器確定從服務中移除servlet時,可以通過調用destroy()方法將釋放servlet占用的任何資源和保存的持久化狀態等。調用destroy方法之前必須保證當前所有正在執行service方法的線程執行完成或者超時;
之后servlet實例可以被垃圾回收,當然什么時候回收並不確定,因此destroy方法是是否必要的。

(3)運行原理:

當Web服務器接收到一個HTTP請求時,它會先判斷請求內容——如果是靜態網頁數據,Web服務器將會自行處理,然后產生響應信息;如果牽涉到動態數據,Web服務器會將請求轉交給Servlet容器。此時Servlet容器會找到對應的處理該請求的Servlet實例來處理,結果會送回Web服務器,再由Web服務器傳回用戶端。

針對同一個Servlet,Servlet容器會在第一次收到http請求時建立一個Servlet實例,然后啟動一個線程。第二次收到http請求時,Servlet容器無須建立相同的Servlet實例,而是啟動第二個線程來服務客戶端請求。所以多線程方式不但可以提高Web應用程序的執行效率,也可以降低Web服務器的系統負擔。

下圖粗暴解釋了請求到容器流程

這里寫圖片描述

下圖解釋了請求到容器到servlet周期流程

這里寫圖片描述

文字解說:

1.客戶發出請求—>Web 服務器轉發到Web容器Tomcat;

2.Tomcat主線程對轉發來用戶的請求做出響應創建兩個對象:HttpServletRequest和HttpServletResponse;

3.從請求中的URL中找到正確Servlet,Tomcat為其創建或者分配一個線程,同時把步驟2創建的兩個對象傳遞給該線程;

4.Tomcat調用Servlet的servic()方法,根據請求參數的不同調用doGet()或者doPost()方法;

5.假設是HTTP GET請求,doGet()方法生成靜態頁面,並組合到響應對象里;

Servlet線程結束時:Tomcat將響應對象轉換為HTTP響應發回給客戶,同時刪除請求和響應對象。

可以理解Servlet的生命周期:Servlet類加載(對應3步);Servlet實例化(對應3步);調用init方法(對應3步);調用service()方法(對應4、5步);;調用destroy()方法(對應6步)。

注意:

1.創建Servlet對象的時機:

Servlet容器啟動時:讀取web.xml配置文件中的信息,構造指定的Servlet對象,創建ServletConfig對象,同時將ServletConfig對象作為參數來調用Servlet對象的init方法。

在Servlet容器啟動后:客戶首次向Servlet發出請求,Servlet容器會判斷內存中是否存在指定的Servlet對象,如果沒有則創建它,然后根據客戶的請求創建HttpRequest、HttpResponse對象,從而調用Servlet 對象的service方法。

Servlet Servlet容器在啟動時自動創建Servlet,這是由在web.xml文件中為Servlet設置的屬性決定的。從中我們也能看到同一個類型的Servlet對象在Servlet容器中以單例的形式存在。

2.在Servlet接口和GenericServlet中是沒有doGet()、doPost()等等這些方法的,HttpServlet中定義了這些方法,但是都是返回error信息,所以,我們每次定義一個Servlet的時候,都必須實現doGet或doPost等這些方法。我們經常使用的httpServlet是繼承於GenericServlet實現的。


二、剖析JSP

(1)概述:

JSP和Servlet的本質是一樣的,因為JSP最終需要編譯成Servlet才能運行,換句話說JSP是生成Servler的草稿文件。

JSP就是在HTML中嵌入Java代碼,或者使用JSP標簽,包括使用用戶自定義標簽,從而可以動態的提供內容。早起JSP應用比較廣泛,一個web應用可以全部由JSP頁面組成,只需要少量的JavaBean即可,但是這樣導致了JSP職責過於復雜,這是Java EE標准的出現無疑是雪中送炭,因此JSP慢慢發展成單一的表現技術,不再承擔業務邏輯組件以及持久層組件的責任。

原理概述:(一會詳解)

JSP的本質是servlet,當用戶指定servlet發送請求時,servlet利用輸出流動態生成HTML頁面。由於包含大量的HTML標簽。靜態文本等格式導致servlet的開發效率極低,所有的表現邏輯,包括布局、色彩及圖像等,都必須耦合在Java代碼中,起靜態的部分無需Java程序控制,只有那些需要從數據庫讀取或者需要動態生成的頁面內容才使用Java腳本控制。
因此,JSP頁面內容有以下兩部分組成:

靜態部分:HTML標簽

動態部分:Java腳本

(2)基本知識:

指令就省略了吧,隨便查都有一堆。

重點講講它的內置對象:

首先,我們可以自己去一個目錄去看看jsp編譯成servlet的代碼。目錄是:你的eclipse的工作目錄下:比如:E:\eclipse\workplace.metadata.plugins\org.eclipse.wst.server.core\tmp0\work\
從中,我們可以看到有九個隱藏對象,一些就final了,一些沒有。

1.request(使用最多):HttpServletRequest的一個對象(在JSP頁面可能會用到)。

Request范圍只針對服務器端跳轉。用於接收客戶端發送而來的請求信息。
注意:單一的參數可以使用getParameter()接收,而一組參數要用getParameterValues()接收。但要小心,如果getParameter和getParameterValues接收參數時,返回內容是null,就可能產生NullPointerException,所以最好判斷接收來的參數是否為null。
獲取頭信息的名稱,可使用request的getHeaderNames()方法;而要想取出每個頭信息的內容則需使用getHeader()方法。比如:語言、主機、Cookie等。

2.Response:

HttpServletResponse的一個對象(在JSP頁面中幾乎不會調用response的任何方法)

主要作用:對客戶端的請求進行回應,將Web服務器處理后的結果發回給客戶端。
設置頭信息:客戶端與服務器端經常需要發送許多額外信息。服務器端可通過setHeader方法,將頭信息設置為refresh,並指定刷新時間,還有跳轉的路徑URL。如:例子就是那些頁面經常提示的“3秒后跳轉到首頁”這樣的操作。
如果定時為0,則為無條件跳轉。注意:定時跳轉屬於客戶端跳轉。而且這種設置跳轉頭信息的方式,單純html也可以做,所以要結合實際考慮,如需請求的是動態頁則需JSP進行編寫

3.pageContext:

頁面的上下文,表示當前頁面,是一個PageContext的一個對象,可以從該對象中獲取到其他8個隱含對象,也可以從中獲取到當前頁面的其他信息。(學習自定義標簽時使用它,JSP頁面上很少直接使用,1`但很重要)。作用范圍僅在當前頁面。實際上pageContext可以設置任意范圍的屬性,而其他操作也是對這一功能的再度包裝而已。但一般習慣於使用pageContext對象設置保存在一頁范圍的屬性。很少使用他進行設置其他范圍的屬性。

4.session:

代表瀏覽器和服務器的一次會話,是HttpSession的一個對象,后面詳細學習。這個session屬性設置后,可在任何一個與設置頁面相關的頁面中獲取。也就是不管是客戶端跳轉還是服務器端跳轉都可以取得屬性。但是如果再打開一個新的瀏覽器訪問該jsp頁面,則無法取得session屬性。因為每個新的瀏覽器連接上服務器后就是一個新的session。

5.application:

代表當前web應用。是ServletContext對象。這個設置的屬性可讓所有用戶(session)都看得見。這樣的屬性保存在服務器上。

6.config:

前JSP對應的Servlet的ServletConfig對象(開發的時候幾乎不用)。若需要訪問當前JSP配置的初始化參數,需要通過映射的地址才可以。
映射JSP方式:

這里寫圖片描述

7.out:

作用:完成頁面的輸出操作。但在開發中,一般是使用表達式完成輸出的。
JspWriter對象,經常調用out.println() 可以直接把字符串打印到瀏覽器上。

8.page

指向當前JSP對應的Servlet對象的引用,但為Object類型,只能調用Object類的方法(幾乎不使用)。就是當前JSP對象。

9.exception:

在聲明了page 指令的isErrorPage=”true”時,才可以使用。<%@ page isErrorPage=”true”%>

大致使用頻率:

pageContext,request,session,application;(對屬性的作用域的范圍從小到大)

out,response,config,page,exception

(3)JSP運行原理:

這里寫圖片描述 
這里寫圖片描述

1.WEB容器(Servlet引擎)接收到以.jsp為擴展名的URL的訪問請求時,容器會把訪問請求交給JSP引擎去處理

2.每個JSP頁面在第一次被訪問時,JSP引擎將它翻譯成一個Servlet源程序,接着再把這個Servlet源程序編譯成Servlet的.class類文件,然后再由WEB容器(Servlet引擎)像調用普通Servlet程序一樣的方式來裝載和解釋執行這個由JSP頁面翻譯成的Servlet程序,並執行該servlet實例的jspInit()方法(jspInit()方法在Servlet的生命周期中只被執行一次)。。

3.然后創建並啟動一個新的線程,新線程調用實例的jspService()方法。(對於每一個請求,JSP引擎會創建一個新的線程來處理該請求。如果有多個客戶端同時請求該JSP文件,則JSP引擎會創建多個線程,每個客戶端請求對應一個線程)。

4.瀏覽器在調用JSP文件時,Servlet容器會把瀏覽器的請求和對瀏覽器的回應封裝成HttpServletRequest和HttpServletResponse對象,同時調用對應的Servlet實例中的jspService()方法,把這兩個對象作為參數傳遞到jspService()方法中。

5.jspService()方法執行后會將HTML內容返回給客戶端。

如果JSP文件被修改了,服務器將根據設置決定是否對該文件進行重新編譯。如果需要重新編譯,則將編譯結果取代內存中的Servlet,並繼續上述處理過程。 如果在任何時候由於系統資源不足,JSP引擎將以某種不確定的方式將Servlet從內存中移去。當這種情況發生時,jspDestroy()方法首先被調用, 然后Servlet實例便被標記加入“垃圾收集”處理。

補充:

1.JSP規范也沒有明確要求JSP中的腳本程序代碼必須采用Java語言,JSP中的腳本程序代碼可以采用Java語言之外的其他腳本語言來編寫,但是JSP頁面最終必須轉換成JavaServlet程序。

2.可以在WEB應用程序正式發布之前,將其中的所有JSP頁面預先編譯成Servlet程序。

3.以多線程方式執行可大大降低對系統的資源需求,提高系統的並發量及響應時間,但應該注意多線程的編程限制,由於該Servlet始終駐於內存,所以響應是非常快的。

4.雖然JSP效率很高,但在第一次調用時由於需要轉換和編譯而有一些輕微的延遲。在jspInit()中可以進行一些初始化工作,如建立與數據庫的連接、建立網絡連接、從配置文件中獲取一些參數等,而在jspDestory()中釋放相應的資源。


參考博客:

http://blog.csdn.net/fengdongkun/article/details/8159381

http://www.cnblogs.com/mlloc-clove/p/3549777.html


好了,JavaWeb–深入Servlet與JSP(運行原理)講完了。本博客是我復習階段的一些筆記,拿來分享經驗給大家。歡迎在下面指出錯誤,共同學習!!你的點贊是對我最好的支持!!

轉載:JackFrost的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM