1. 程式人生 > >JSP執行原理分析

JSP執行原理分析

JSP執行原理分析

我們在開發Java Web的過程中,可能有過這樣的疑問,Tomcat是一個Servlet執行環境(容器),所有經過Tomcat的請求都是有一個servlet來處理的。servlet是一個Java類,可是jsp不是。那jsp又是怎麼在Tomcat裡面執行的呢?事實上,JSP是Servlet的一種特殊形式,每個JSP頁面就是一個Servlet例項——JSP頁面由系統編譯成Servlet,Servlet再負責響應使用者請求。JSP其實也是Servlet的一種簡化,使用JSP時,其實還是使用Servlet,因為Web應用中的每個JSP頁面都會由Servlet容器生成對應的Servlet。根據上面的分析,我們又有一個疑問:為什麼說jsp就是一個Servlet,是根據什麼來判定的呢?

  1. 為什麼說jsp是Servlet?

首先我們在IntelljIdea裡面編寫一個簡單的test.jsp頁面

當啟動Tomcat後,可以在Tomcat的\work\Catalina\localhost\ROOT\org\apache\jsp目錄下找到如下檔案

這兩個檔案便是test.jsp經過系統編譯生成的對應的 .java檔案和 .class檔案,我們開啟test_jsp.java檔案

可以看到,這個test_jsp類繼承了HttpJspBase這個類以及實現了org.apache.jasper.runtime.JspSourceDependent與org.apache.jasper.runtime.JspSourceImports這兩個介面,我們知道判定一個類是Servlet的方法是看這個類是否間接或直接實現了Servlet介面,所以我們要看test_jsp這個類是否間接或直接實現了該介面,這裡它並沒有直接實現Servlet介面,所以現在我們還不能從這裡看出test_jsp這個類就是一個Servlet,我們便可以猜想,會不會是HttpJspBase這個類實現了此介面呢。於是我們進一步分析HttpJspBase這個類的繼承結構。

1.1HttpJspBase繼承結構

 我們去檢視HttpJspBase類的原始碼

從上面就可以看出,該類繼承了HttpServlet,我們之前便知道HttpServlet間接實現了Servlet介面。如果不知道的我們可以在IntelljIdea裡面將滑鼠定位在HttpJspBase上面按住ctrl+alt+u檢視他的繼承關係圖:

圖 1-1 HttpJspBase繼承關係

到這裡我們便能清晰的知道為什麼說jsp是一個Servlet了。

  1. Tomcat是如何處理jsp檔案的?

經過上面的分析,我們已經知道了jsp就是一個Servlet。那jsp又是如何轉換成Servlet的呢?我們在學習以前的學習中知道了我們寫的每一個Servlet都需要在web.xml 檔案裡面的去做配置,這樣瀏覽器才能訪問得到這個Servlet。可是現在這個test.jsp並沒有在我們專案的web.xml裡面做任何配置,那瀏覽器是怎麼訪問到jsp頁面的呢?回答這個問題之前我們先來了解一下Tomcat如何響應靜態資源。

2.1 Tomcat如何響應靜態資源?

2.1.1、全域性web.xml解析

本質上講,Tomcat對於所有的靜態資源會做統一處理。也就是在所有你沒有配置URL匹配的地方,Tomcat這個全域性統一處理的配置就開始接管工作了。在Tomcat的conf目錄下面我們可以看到有一個web.xml檔案,開啟後你會發現這樣的說明:

在向下你會看到關於這個全域性處理的Servlet宣告,如下圖

這個DefaultServlet的servlet-mapping是這樣配置的:

到這裡不禁有人會問,既然url-pattern 配置的是  / ,那不就應該可以響應所有的請求了嗎?其實上面的圖中已經給出瞭解釋,事實上這是匹配所有你沒有定義的Servlet-mapping的請求。之所以自己定義的Servlet可以優先生效,則是因為Tomcat內的Servlet   的mapping配置是嚴格按照宣告順序初始化,並按此順序響應請求,一層層按此比對,有一個可以響應請求,就用其處理。有相關疑問可以參考一下博文:

http://k1121.iteye.com/blog/1564241

我們簡單總結一下:所有經過Tomcat的請求都是有一個servlet來處理的。如果一個請求沒有匹配到任何應用指定的servlet,那麼就會流到Tomcat的預設的servlet來,這個Servlet名字叫做DefaultServlet,DefaultServlet是配置在/conf/web.xml裡面。

2.2、Tomcat如何響應jsp請求?

上一節我們瞭解到Tomcat使用DefaultServlet處理所有的靜態資源。這一節我們來看一個jsp請求又是怎麼被響應的。同DefaultServlet類似,jsp的處理也不需要我們單獨配置,而是在/conf/web.xml中做為全域性配置存在。其對應的處理類為JspServlet,用於處理所有的jsp請求。同樣我們開啟/conf/web.xml,可以看到以下程式碼與註釋

通過看註釋我們便對該配置的作用一目瞭然,往下看我們會看到JspServlet的mapping配置,其url-pattern為*.jsp和*.jspx。所以它可以攔截所有的jsp請求並作出相應的反應。

到這裡我們便知道了為什麼瀏覽器在我們自己沒對jsp檔案做任何配置的情況下依舊能訪問的原因。

2.3、jsp如何轉換成Servlet?

在文章開頭我們知道當Tomcat啟動過後,一個xxx.jsp檔案會在\org\apache\jsp目錄下生成相應的xxx_jsp.java檔案與xxx_jsp.class檔案,開啟我們之前已經生成的test_jsp.java 檔案,這個檔案結構如下圖所示:

主要的轉換動作是在方法_japService()中實現的,如下的Servlet類的程式碼截圖可以看出,其中插入了session、application等物件的初始化,這幾個物件都是通過頁面級別的物件pageContext獲取到的。

 

頁面中的java程式碼去哪兒了呢,轉換過程中,HTML頁面元素內容可以理解為通過out.write()直接輸出給前端頁面,java程式碼(<%%>包含的內容)直接去掉<%%>寫到類中執行。部分程式碼截圖如下。

圖中紅色方框內的內容就是我們在jsp頁面中<% %>中的Java程式碼。在轉化中直接去除<% %>後放到類程式碼中,而其餘的可以理解為直接out.write()輸出給前端頁面。至此我們便解釋了Tomcat如何處理jsp檔案的問題。

3.總結

本文對jsp的執行原理進行了詳細的分析。我們可以得出下面的流程圖:

圖3-1 jsp頁面工作原理圖

根據上面的JSP頁面工作原理圖,可以得到如下結論:

    — JSP檔案必須在JSP伺服器內執行。

    — JSP檔案必須生成Servlet才能執行。

JSP和Servlet會有如下轉換:

    - JSP頁面的靜態內容、JSP指令碼都會轉換成Servlet的xxxService()方法,類似於自行建立Servlet時service()方法。

    - JSP宣告部分,轉換成Servlet的成員部分。所有JSP宣告部分可以使用private,protected,public,static等修飾符,其他地方則不行。

    - JSP的輸出表達式(<%= ..%>部分),輸出表達式會轉換成Servlet的xxxService()方法裡的輸出語句。

    - 九個內建物件要麼是xxxService()方法的形參,要麼是該方法的區域性變數,所以九個內建物件只能在JSP指令碼和輸出表達式中使用。