Tomcat伺服器Day03-Jasper引擎

攻城獅Chova發表於2020-11-06

基本概念

  • 對於基於JSPWeb應用,可以在JSP頁面直接編寫Java程式碼,新增第三方庫,使用EL表示式.但是最終輸出到客戶端瀏覽器的都是標準的HTML頁面,包括js,css等等.並不包含Java相關的語法 .JSP可以看作是一種執行在伺服器端的指令碼,最終以HTML頁面方式響應給客戶端
  • 使用Tomcat中的Jasper引擎將jsp檔案轉換為HTML頁面檔案:
    • JSP本質上是一個Servlet
    • Tomcat使用JasperJSP語法進行解析,生成Servlet並生成Class位元組碼檔案
    • 使用者在訪問jsp檔案時,會訪問轉換後的Servlet, 最終的訪問結果以HTML頁面的方式直接響應在瀏覽器端
    • 在執行過程中 ,Jasper引擎會檢測jsp檔案是否修改,如果修改則重新編譯jsp檔案

編譯方式

執行時編譯

  • Tomcat不會在啟動Web應用時自動編譯JSP檔案,而是在客戶端第一次請求時,才編譯需要訪問的JSP檔案

編譯過程

  • Tomcat在預設的web.xml中配置了org.apache.jasper.servlet.JspServlet, 用於處理所有的 .jsp.jspx結尾的請求
  • JspServlet的實現就是執行編譯時的入口
<servlet>
	<servlet-name>jsp</servlet-name>
	<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
	<init-param>
		<param-name>fork</param-name>
		<param-value>false</param-value>
	</init-param>
	<init-param>
		<param-name>xpoweredBy</param-name>
		<param-value>false</param-value>
	</init-param>
	<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>jsp</servlet-name>
	<url-pattern>.jsp</url-pattern>
	<url-pattern>.jspx</url-pattern>
</servlet-mapping>
  • JspServlet請求處理流程:
    在這裡插入圖片描述

編譯結果

  • 如果在tomcat/conf/web.xml 中配置了引數scratchdir,jsp編譯後的結果會輸出到配置的目錄下:
<init-param>
	<param-name>scratchdir</param-name>
	<param-value>e:/jsp/</param-value>
</init-param>
  • 如果沒有配置該選項,則jsp編譯後的結果,將會存放在Tomcat的安裝目錄的work/Catalina/localhost/ 目錄下

預編譯

  • 預編譯: 直接在web專案啟動時,一次性將web應用用的所有jsp頁面一次性編譯完成.這樣在web專案執行過程中,可以不再需要實時編譯,而是直接呼叫jsp頁面對應的servlet完成請求處理,從而提升系統效能
  • 要想進行預編譯,必須首先確保下載並安裝了Apache Ant
  • Tomcat中提供了一個shell程式JspC用於支援jsp編譯,而且在Tomcat安裝目錄下提供了一個catalina-tasks.xml檔案宣告瞭Tomcat支援的Ant任務,這樣很容易使用Ant來執行jsp的預編譯

編譯原理

程式碼分析

  • 生成的Java檔案的類名為index_jsp.java, 繼承自org.apache.jasper.runtime.HttpJspBase, 該類是HttpServlet的子類.所以jsp的本質就是一個servlet
  • 屬性 _jspx_denpendants儲存了當前jsp頁面依賴的資源,包含引入的外部jsp頁面,匯入的標籤,標籤所在的jar包.便於後續處理過程中使用. 比如以Map形式儲存了每個資源的上次修改時間便於重新編譯檢測
  • 屬性 _jspx_imports_packages存放匯入的java包,預設匯入javax.servlet, javax.servlet.http, javax.servlet.jsp
  • 屬性 _jspx_imports_classes存放匯入的類 ,jsp頁面中通過import標籤匯入的類都會包含在該集合 . _jspx_import_packages_jspx_import_classes屬性主要用於配置EL引擎上下文
  • 請求處理由 _jspService方法完成,在父類HttpJspBase中的service方法通過模板方法模式,呼叫了子類的 _jspService方法
  • _jspService方法中定義了幾個重要的區域性變數 : pageContext, Session, application, config, out, page. 因為整個頁面的輸出都是由 _jspService方法完成,因此這些變數和引數會對整個jsp頁面生效. 這個就是在jsp頁面中能夠使用變數的原因
  • jsp頁面中指定文件型別的page變標籤的值最終作為response.setContentType() 使用
  • 對於生成的html檔案的靜態內容,呼叫out.write() 輸出
  • 對於 <% …%> 標籤中的程式碼,直接轉換為Servlet類中的程式碼,如果在程式碼中嵌入了靜態檔案,同樣會呼叫out.write() 輸出

編譯流程

  • Jasper的編譯流程主要包括程式碼生成和編譯兩部分
    在這裡插入圖片描述
  • Compiler通過一個PageInfo物件儲存jsp頁面編譯過程中的各種配置. 這些配置可以是來自於web應用的初始化引數,也可以是來自於jsp頁面的標籤指令配置,比如page, include
  • 呼叫ParseController解析標籤指令節點,驗證標籤指令是否合法,同時將配置資訊儲存到PageInfo中,用於控制程式碼生成
  • 呼叫ParseController解析整個jsp頁面,由於jsp是逐行解析,所以會對每一行建立一個具體的Node物件,比如靜態文字TemplateText, Java程式碼Scriptlet, 定製標籤CustomTag, Include標籤指令IncludeDirective
  • 驗證標籤指令外的其餘節點的合法性. 比如指令碼,定製標籤 ,EL表示式等
  • 獲取標籤指令以外的其餘節點的頁面配置資訊
  • 編譯並載入當前jsp頁面依賴的標籤
  • 對於jsp頁面的EL表示式,生成對應的對映函式
  • 生成jsp頁面對應的servlet原始碼
  • 程式碼生成完成後 ,Compiler會生成SMAP資訊. 如果配置生成了SMAP資訊 ,Compiler則會在編譯階段將SMAP資訊寫到class檔案中
  • 在編譯階段 ,Compiler的兩個實現AntCompilerJDTCompiler分別呼叫相關框架的API進行原始碼解析
    • AntCompiler通過構造一個Antjavac任務完成編譯
    • JDTCompiler通過呼叫org.eclipse.jdt.internal.compiler.Compiler完成編譯

相關文章