1. 程式人生 > >為什麼JSP渲染比常用模板(freemarker\Velcoity\beetl)慢

為什麼JSP渲染比常用模板(freemarker\Velcoity\beetl)慢

提升渲染效能方法有: 模板引擎將模板檔案編譯成class執行。 模板中的靜態部分採用二進位制輸出,不需要CPU執行的時候再轉碼 合併模板中的靜態部分一起輸出,而不是每一行每一行輸出

第一: JSP對靜態文字處理的不夠好

如果你看看JSP編譯的後的java程式碼(以Tocmat7為例),你會發現,JSP並沒有優化好靜態文字輸出。如下一個JSP程式碼

  1. <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  2.     pageEncoding="UTF-8"%> 
  3. <html> 
  4. <head> 
  5. <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"
  6. <title>Test JSP</title> 
  7. </head> 
  8. <body> 
  9. <% 
  10. String a = "Test JSP"
  11. %> 
  12. <%=a %> 
  13. </body> 
  14. </html> 

Tomcat7 會編譯成為

  1. out.write("<html>\r\n"); 
  2.       out.write("<head>\r\n"); 
  3.       out.write("<meta http-equiv=\"Content-Type\"
  4.             content=\"text/html; charset=ISO-8859-1\">\r\n"); 
  5.       out.write("<title>Test JSP</title>\r\n"); 
  6.       out.write("</head>\r\n"); 
  7.       out.write("<body>\r\n"); 
  8. String a = "Test JSP"
  9.       out.write('\r'); 
  10.       out.write('\n'); 
  11.       out.print(a ); 
  12.       out.write("\r\n"); 
  13.       out.write("</body>\r\n"); 
  14.       out.write("</html>"
    ); 

    可以看出,對於靜態文字,JSP會多次呼叫out.write方法,而write方法內部,每次呼叫,都會做flush檢測等耗時機制。因此,更好的方式應該是將靜態文字合併一次性輸出,應該是下面這種會更好點

// 期望JSP的樣子

  1. out.write("<html>\r\n<head>\r\n ....<body>\r\n“); 
  2. String a = "Test JSP"
  3. out.write("\r\n“); 
  4. out.print(a ); 
  5. out.write("\r\n</body>\r\n</html>");

第二  就算JSP的實現做了如上更改,靜態文字處理還有優化空間。這是因為網際網路傳輸的二進位制,因此會存在一個將靜態文字轉成 byte[] 輸出的過程,這是一個耗費CPU操作的過程,也就是JSP裡的write操作,內部還大量的編碼,而且,隨著JSP一次次渲染,編碼是一次一次重複,實驗 證明,這極大的降低了JSP效能。通過如下虛擬碼可以驗證

  1. publicstaticvoid main(String[] args)throws Exception { 
  2.   String text = "<html>你好中文!你好中文!你好中文!</html>"
  3.   { 
  4.    //模擬jsp
  5.    long start = System.currentTimeMillis(); 
  6.    for(int i=0;i<1000000;i++){ 
  7.     byte[] bs = text.getBytes("UTF-8"); 
  8.     write(bs); 
  9.    } 
  10.    long end = System.currentTimeMillis(); 
  11.    System.out.println("jsp total="+(end-start)); 
  12.   } 
  13.   { 
  14.    // 模擬beetl
  15.    long start = System.currentTimeMillis(); 
  16.    byte[] bs = text.getBytes("UTF-8");   
  17.    for(int i=0;i<1000000;i++){   
  18.     write(bs); 
  19.    } 
  20.    long end = System.currentTimeMillis(); 
  21.    System.out.println("beetl total="+(end-start)); 
  22.   } 
  23. publicstaticvoid write(byte[] bs){ 

輸出是:

  • jsp total=228

  • beetl total=3

可見Beetl將靜態文字預先編碼成二進位制,會提高效能很多。而通常JSP,總是靜態文字多過JSP Code的

第三,JSP在JSTL做的不夠完美,也導致效能很差

由於JSP是基於Java語言,語言本身是OO的,很多地方不適合模板場景使用,因此,自然而然採用JSTL來彌補JSP的不足,這也是後來很多專案都基本上採用了JSTL來寫模板。然而,JSTL的效能更加有問題。比如下一個簡單的JSTL判斷

  1. <c:choose> 
  2.    <c:when test="${param.newFlag== '1' || param.newFlag== '2'}">  
  3.     <th>1 or 2  <font color="Red">*</font> 
  4.    </c:when>  
  5. </c:choose> 

在我最初的想象裡,我認為jsp至少會編譯成如下程式碼:

//期望JSP能編譯成如下程式碼

  1. if(request.getParameter("newFlag").equals("1"
  2.    ||request.getParameter("newFlag").equals("2")){ 
  3.     out.print(...) 

但事實並不是這樣,對於如上JSTL,編譯成

  1. // 實際上JSP編譯的程式碼
  2. out.write((java.lang.String) 
  3.   org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate( 
  4. "${param.newFlag== '1' || param.newFlag== '2'}"
  5. java.lang.String.class
  6. (javax.servlet.jsp.PageContext)_jspx_page_context, nullfalse)); 
也就是,JSP並沒有如預期的預編譯成java程式碼,而是動態解釋執行了 test條件