避免不必要的JSP重新編譯

來源:互聯網
上載者:User

關於JavaServer頁面(JSP)新聞群組的最常見的一個問題與重新編譯有關。不想重新編譯JSP,卻又不得不這樣做,這是許多開發人員所面對的煩惱。本文將描述造成重新編譯的情境,並從解釋WebLogic JSP容器的內部操作開始,介紹每個顯然“不受歡迎的”情境,並應用程式容器的到期檢查演算法(Stale Checking Algorithm)。此外,本文還將討論控制JSP和servlet類重載的參數。對以生產模式下啟動並執行伺服器,極力推薦這麼做。

JSP容器的到期檢查機制
  在WebLogic中,JSP被編譯成.class檔案。我們使用的術語到期檢查機制(Stale Checking Mechanism)指的是用來判斷某一特殊JSP .class檔案是否比當前JSP檔案更舊(“到期”)的邏輯。WebLogic JSP容器確保任何JSP及其相關檔案只在修改後才能被重新編譯。查看產生的Java代碼是瞭解JSP容器內部操作的一個好方法。我們將採用一個JSP作為例子,使用命令列JSP編譯器編譯它,並查看產生的原始碼。JSP編譯器(WebLogic.jspc)是隨標準WebLogic 伺服器安裝工具箱一起提供的。

考慮一個稱為foo.jsp的簡單JSP頁面:

A simple JSP page

  現在使用命令列JSP編譯器來編譯這個JSP,指定一個稱為keepgenerated的選項,顧名思義,該選項為JSP頁面產生相應的Java代碼,並將代碼儲存在磁碟上。

java weblogic.jspc -keepgenerated -d ./WEB-INF/classes foo.jsp[jspc]warning: expected file /WEB-INF/web.xml not found,tag libraries cannot be resolved.<Jul 11, 2004 7:29:26 PM PDT> <Warning> <HTTP><BEA-101181> <Could not find web.xml under WEB-INF in the doc root: ..>

  編譯器在作為上述選項指定的輸出目錄(-d)中產生.java檔案及其對應的.class檔案。它將產生的類檔案放在稱為jsp_servlet的包中,這恰巧是預設的JSP包首碼(除非在weblogic.xml中覆蓋),因此,產生的Java檔案可在./WEB-INF/classes/jsp_servlet中找到,並且稱為__foo.java。
  請注意,我們可忽略編譯器發出的關於未找到web.xml檔案的警告,因為此時我們並沒有真正使用標籤庫。
  在產生代碼(__foo.java)中,與我們的討論最相關的部份就是staticIsStale()方法,如下所示。

清單1. staticIsStale()方法

public static boolean _staticIsStale(weblogic.servlet.jsp.StaleChecker sci) {if (sci.isResourceStale("/foo.jsp", 1089594167518L, "8.1.2.0","America/Los_Angeles"))return true;return false;}

  從上面一小段代碼中顯然可以看出,調用weblogic.servlet.jsp.StaleChecker介面上的isResourceStale()方法是為了確定JSP是否已修改過。isResourceStale()方法的參數如下所示,這些參數是按以下順序出現的:

 

1、 要檢查的資源,比如,/foo.jsp。
2、 JSP頁面的時間戳記(長整型)。
3、 WebLogic Release Build版。
4、 當前機器的預設時區。

  JSP容器通過實現StaleChecker介面調用_staticIsStale()方法。該實現接收一個帶有清單1中所示參數的回調(isResourceStale())。有了這些參數,該實現可以僅接收所有必需的資訊,以推斷給定資源是否到期。當資源(參數1)/foo.jsp的時間戳記(參數2)比儲存在已編譯類檔案中的時間戳記還要新(參數更大)時,或者當發行版本不同時,JSP容器認為JSP.class檔案“到期”。

  讓我們看它的一些重要結論:

  • 因為JSP頁面的時間戳記儲存在類檔案內部,並且是在編譯時間計算的,所以修改類檔案的時間戳記不會對到期檢查過程產生影響。(這是一種很常見的荒謬說法,但願上面的例子清楚地駁斥了它。)
  • 第4個參數,也就是時區,只在以存檔格式(.war)進行部署時使用。
  • WebLogic發行版本隨每個服務包改變,因此需要為每個服務包重新編譯所有JSP。提出這個要求是為了確保JSP類可以利用較新服務包或發行版本中的所有編譯器缺陷修複或所有JSP運行時更改。

靜態包含怎樣?
  人們會問的下一個合乎邏輯的問題是:即使只修改了特定JSP頁面的一個靜態包含檔案,JSP也會重新編譯這個頁面嗎?回答是肯定的。即使是修改了像靜態包含這樣的相關檔案,也要重新編譯整個頁面(稱為“編譯單元”更合適)。要瞭解容器如何處理這種依賴性,請考慮以下包含名為baz.inc的靜態包含檔案的JSP。

清單2. foo.jsp

A simple jsp page.<%@ include file="baz.inc"%>

 

清單3. baz.inc

--Simple Static Include--

  為JSP編譯器對foo.jsp重運行上述命令列,現在會產生一個Java檔案,它包含如下所示的一小段有趣代碼。如您所見,每個從屬物都是用_staticIsStale()方法處理的,這樣,即使修改了一個從屬物(這裡是baz.inc),也要重新編譯整個JSP或“編譯單元”。JSP容器期望根JSP頁面(foo.jsp)返回一個指示它是否到期的布爾值。因此,每個產生的類檔案都會產生檢驗其所有相關檔案的代碼。

public static boolean _staticIsStale(weblogic.servlet.jsp.StaleChecker sci) {if (sci.isResourceStale("/foo.jsp", 1089616972487L, "8.1.2.0", "America/Los_Angeles"))return true;if (sci.isResourceStale("/baz.inc", 1089616984268L, "8.1.2.0", "America/Los_Angeles"))return true;return false;}

  總之,WebLogic JSP容器讓每個JSP .class維護自己的從屬物列表,並根據這個列表來儲存原始JSP(及其從屬物)的狀態(時間戳記)。容器對JSP .class調用_staticIsStale()方法,然後JSP .class回調JSP容器,並通過weblogic.servlet.jsp.StaleChecker.isResourceStale()返回判斷單個資源是否到期所需的所有資訊。這大大簡化了到期檢查任務,而且消除了在單獨某個位置上為每個JSP維護時間戳記的需要。

導致重新編譯JSP的情境
  我們已經分析了JSP容器執行到期檢查時要考慮的一些因素。現在,讓我們來看看重新編譯JSP的一些常見情境:

1. 使用構建指令碼的檔案副本可修改JSP的時間戳記。這可導致重新編譯所有JSP。
  考慮一下所有JSP都位於名為src的目錄中的情境。假定某一構建指令碼複製了所有JSP,並將servlet Java檔案編譯到構建目錄中。然後該指令碼在src目錄之上運行weblogic.jspc,並將所有已編譯的JSP放入構建目錄中。在這裡,將JSP複製到構建目錄中很可能改變了JSP的時間戳記(除非構建指令碼使用cp –p/-m保留檔案時間戳記)。當該Web應用程式從構建目錄部署到伺服器上時,要重新編譯所有JSP,因為JSP類是用比已部署的JSP更舊的JSP(也就是這裡複製到構建目錄下的JSP)來編譯的。這是最常見的重新編譯的情況之一,它可通過確保執行複製操作時保留了檔案時間戳記來避免。

2. 修改weblogic.xml的packagePrefix參數將導致重新編譯。到期檢查機制負責尋找特殊Web應用程式weblogic.xml檔案中的packagePrefix,並為/foo.jsp搜尋名為<packagePrefix>.__foo.class的類。假定我們使用weblogic.jspc 預構建所有JSP,將它們放在Web應用程式的WEB-INF/classes目錄中,然後我們將該Web應用程式歸檔(WAR)部署到伺服器上。假定我們在這個Web應用程式中有一個名為foo.jsp的JSP。在weblogic.xml中缺乏“packagePrefix”的情況下,到期檢查機制將尋找jsp_servlet.__foo.class類。現在假定我們修改weblogic.xml並添加一個包首碼,比如說com.bar,然後將同一WAR重新部署到伺服器。此時訪問foo.jsp將導致重新編譯JSP,因為到期檢查機制將尋找名為“com.foo.__foo.class”的類。通過確保調用weblogic.jspc命令時使用了-package參數並使用了相同的包名稱,可避免這個問題。

3. 修改weblogic.xml的workingDir參數也會導致重新編譯。在這種情況下,除了通常的Web應用程式類路徑之外,JSP容器還將在新的“workingDir”中尋找JSP類。因為在部署新版本之前,原先使用的目錄中有JSP類,但JSP容器無法找到它們,因此將重新編譯請求的JSP。
  注意:情境2和情境3清楚地解釋了即使在修改weblogic.xml時,也需要重構建或先行編譯Web應用程式。在完成所有修改後部署先行編譯的WAR,確保不用重新編譯JSP。

4. 將預構建的WAR部屬到一個更新版本的WebLogic伺服器會導致重新編譯所有JSP。正如描述到期檢查機制那一節所解釋的,在將JSP部署到不同版本的伺服器時,JSP容器會重新編譯所有JSP。這麼做是為了確保特定版本或服務包中的所有JSP編譯器/運行時增強或缺陷修複在產生的程式碼中可用(沒有這一限制,可能會以一些類在JSP運行時引用不存在的方法而結束操作)。在理想情況下,先行編譯JSP 的構建指令碼必須使用與正在部署的伺服器使用同一版本的weblogic.jar。建議將伺服器使用的所有補丁和周期性修複添加到構建指令碼使用的類路徑下。總之,構建和部署環境必須完全一致。這可以避免部署後遇到任何不必要重新編譯的問題。

進一步控制到期檢查
  在容器執行到期檢查時進行控制可以讓我們調優容器,使它運行得更好,因此為JSP和servlet提供了更好的回應時間。每次到期檢查都要求JSP容器轉到磁碟並為那個特定的JSP重新讀取最後修改時間。當調用太頻繁時,該過程可導致效能下降,因為它影響JSP的回應時間。在理想情況下,需要到期檢查開發期間表現得非常活躍,特別是在應用程式經常改變時。通過單擊瀏覽器上的重新整理/重載,並讓JSP容器重新編譯和重載新頁面,可很好地測試出對特殊JSP所做的修改。但在生產模式下,同樣的做法可能導致效能降低。
  以下參數的預設值最適合開發模式。建議你們在生產環境下進行部署時相應地修改這些值。

PageCheckSeconds
  對已經編譯好的JSP的每個新請求,容器都會從其設定檔(這裡是weblogic.xm)檢查pageCheckSeconds的值,如果上次到期檢查與目前時間之間的間隔大於pageCheckSeconds,那麼還要執行到期檢查操作。例如,假設pageCheckSeconds的值設為10秒。對於針對foo.jsp的請求,容器執行檢查操作,以瞭解目前時間與最後到期檢查之間的間隔是否大於pageCheckSeconds。這裡假定頁面在10秒以前被重新編譯和訪問過;容器將檢查該類是否到期。在此間隔中,不會對foo.jsp的任何請求進行到期檢查。
  如果不是處於開發模式下,而且無需要每隔1秒(預設值)就檢查一次JSP頁面,那麼強烈推薦您將參數值更改為-1(永遠不進行到期檢查)或者更改為像60秒這樣的值。這避免了每次訪問JSP時都調用File.lastModified()、重載JSP類和檢查時間戳記。

清單4. 來自weblogic.xml的程式碼片段,展示了可如何設定該這個參數

<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN""http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd"><weblogic-web-app><jsp-descriptor><jsp-param><param-name>pageCheckSeconds</param-name><param-value>10</param-value></jsp-param></jsp-descriptor></weblogic-web-app>

  請注意,對於Web應用程式中的JSP從不個別改變的生產環境,最好配置容器永不對servlets和JSP執行到期檢查。

servlet-reload-check-secs
  在開發模式中,當servlet被修改和重新編譯到,比方說,迅速增長的WAR的WEB-INF/classes目錄中時,我們期望從瀏覽器執行請求時,容器調用它的最新版本的servlet。為了處理該問題,WebLogic 的Web容器每隔servlet-reload-check-secs間隔就會檢查WEB-INF/classes中是否有檔案被修改過。這個參數的預設值是1秒。對於希望看到對servlet類的最新修改但又不必重新部署應用程式的開發模式而言,這是一個很好的預設值。但在進入生產之前,必須將這個值更改為-1(永不重載servlet檔案)。在生產模式下,個別類不會改變,將servlet-reload-check-secs的值設為-1總是最好的選擇。

清單5. servlet-reload-check-secs值設為-1的weblogic.xml樣本

<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN""http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd"><weblogic-web-app><container-descriptor><servlet-reload-check-secs>-1</servlet-reload-check-secs></container-descriptor></weblogic-web-app>

JSP類載入器
  我們將通過查看WebLogic伺服器如何載入JSP類來結束我們的討論。每個JSP都是在自己的類載入器(通常稱為一次性類載入器)中載入的。該類載入器是Web應用程式類載入器的子載入器,負責載入有關的JSP類及其內部類(如果有的話)。好奇的讀者可能會覺得奇怪,為什麼WebLogic在每個JSP自己的類載入器中載入JSP。真的需要這麼複雜嗎?WebLogic不能只用應用程式類載入器而使得生活更輕鬆嗎?所有這些問題都是有根據的,而且每個尋求WebLogic類載入器天堂的人都應該問自己這些問題。為瞭解決這些問題,讓我們設想我們的Web應用程式有幾個JSP、少數servlet、一個過濾器以及幾百個還包含標籤處理器類的實用類。現在,假設所有這些類都被載入到單個類載入器中。如果修改單個JSP,然後單擊瀏覽器上的重載,那麼下面的事情將不得不發生:

 

  • JSP容器將不得不重新編譯頁面。
  • 將不得不丟棄用來載入較早類版本的整個Web應用程式類載入器。
  • 不得不建立一個新的Web應用程式類載入器,而且要重載和重新初始化所有servlet和JSP(包括剛才修改的那個)。

  Java不允許重用類載入器來重載類的新版本。相反,您不得不放棄類載入器並建立一個新的。基於這個原因,上面的情境非常不合時宜;即使只更改了一個類,應用伺服器也不得不重載大量的類。
  現在,讓我們看看WebLogic是如何?其類載入器方案的。考慮一下前面提及的那個情境。如果我們修改JSP,並碰巧在瀏覽器上進行重載,那麼伺服器將執行以下任務:

  • JSP容器重新編譯頁面。
  • 它丟棄用來載入這個JSP類的舊版本的那個JSP類載入器。
  • 它建立一個將Web應用程式類載入器作為父載入器的新JSP類載入器,並為該頁面提供服務。

  如您所見,只有一個JSP類必須重載,整個Web應用程式類載入器保持不動,並且不受我們對JSP所做小小改動的影響。因此,在修改單個JSP時,容器會丟棄舊的類載入器、重新編譯並只重載為這個JSP產生的類。這避免了重載或拋棄整個Web應用程式類載入器。當只有某一特殊Web應用程式中的某些JSP經常改變時,這是一個很大的勝利。

結束語
  
有了這些關於JSP容器內部組織的知識,不但可以避免遭遇令人不快的、不必要的JSP重新編譯的狀況,而且還可以通過使用pageCheckSeconds和servlet-reload-checks-secs參數改進頁面回應時間自身。

原文出處:
http://dev2dev.bea.com/pub/a/2005/01/jsp_reloaded.html

 作者簡介
  Nagesh Susarla是BEA Systems的WebLogic伺服器開發小組的一名進階軟體工程師。他致力於為WebLogic伺服器設計和實現Servlet/JSP容器,而且還是ANTLR (分析器/產生器)的超級愛好者。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.