茫茫網海中的冷日
         
茫茫網海中的冷日
發生過的事,不可能遺忘,只是想不起來而已!
 恭喜您是本站第 1729118 位訪客!  登入  | 註冊
主選單

Google 自訂搜尋

Goole 廣告

隨機相片
IMG_2567283.jpg

授權條款

使用者登入
使用者名稱:

密碼:


忘了密碼?

現在就註冊!

爪哇咖啡屋 : [轉貼]Template和JSP技術

發表者 討論內容
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Template和JSP技術

Template和JSP技術

2005-02-16
一、起源與現狀:
關於Template和JSP的起源還要追述到Web開發的遠古年代,那個時候的人們用CGI來開發web應用,在一個CGI程序中寫HTML標籤。
在這之後世界開始朝不同的方向發展:sun公司提供了類似於CGI的servlet解決方案,但是無論是CGI還是servlet都面對同一個問題:在程序裡寫html標籤,無論如何都不是一個明智的解決方案。於是sun公司於1999年推出了JSP技術。而在另一個世界裡,以PHP和ASP為代表的scriptlet頁面腳本技術開始廣泛應用。
不過即便如此,問題並沒有結束,新的問題出現了:業務和HTML標籤的混合,這個問題不僅導致頁面結構的混亂,同時也使代碼本身難以維護。
於是來自起源於70年代後期的MVC模式被引入開發。MVC的三個角色:Model——包含除UI的數據和行為的所有數據和行為。View是表示UI中模型的顯示。任何信息的變化都由MVC中的第三個成員來處理——控制器。

在之後的應用中,出現了技術的第一次飛躍:前端的顯示邏輯和後端的業務邏輯分離,COM組件或EJB或CORBA用於處理業務邏輯,ASP、JSP以及PHP被用於前端的顯示。這個就是Web開發的Model 1階段(頁面控制器模式)。
不過這個開發模式有很多問題:
1.頁面中必須寫入Scriptlet調用組件以獲得所必需的數據。
2.處理顯示邏輯上Scriptlet代碼和HTML代碼混合交錯。
3.調試困難。JSP被編譯成servlet,頁面上的調試信息不足以定位錯誤。
這一切都是因為在Model 1中並沒有分離視圖和控制器。完全分離視圖和控制器就成了必須。這就是Model 2。它把Model 1中未解決的問題——分離對組件(業務邏輯)的調用工作,把這部分工作移植到了控制器。現在似乎完美了,不過等等,原來的控制器從頁面中分離後,頁面所需的數據怎麼獲得,誰來處理頁面顯示邏輯?兩個辦法:1. 繼續利用asp,php或者jsp等機制,不過由於它們是運行在web環境下的,他們所要顯示的數據(後端邏輯產生的結果)就需要通過控制器放入request流中;2. 使用新手法——模板技術,使用獨立的模板技術由於脫離的了web環境,會給開發測試帶來相當的便利。至於頁面所需數據傳入一個POJO就行而不是request對象。
模板技術最先開始於PHP的世界,出現了PHPLIB Template和FastTemplate這兩位英雄。不久模板技術就被引入到java web開發世界裡。目前比較流行的模板技術有:XSTL,Velocity,JDynamiTe,Tapestry等。另外因為JSP技術畢竟是目前標準,相當的系統還是利用JSP來完成頁面顯示邏輯部分,在Sun公司的JSTL外,各個第三方組織也紛紛推出了自己的Taglib,一個代表是struts tablib。
二、 模板技術分析:
模板技術從本質上來講,它是一個佔位符動態替換技術。一個完整的模板技術需要四個元素:0. 模板語言,1. 包含模板語言的模板文件,2. 擁有動態數據的數據對象,3. 模板引擎。以下就具體討論這四個元素。(在討論過程中,我只列舉了幾個不同特點技術,其它技術或有雷同就不重複了)

1.模板語言:
模板語言包括:變量標識和表達式語句。根據表達式的控制力不同,可以分為強控制力模板語言和弱控制力模板語言。而根據模板語言與HTML的兼容性不同,又可以分為兼容性模板語言和非兼容性模板語言。
模板語言要處理三個要點:
1. 標量標記。把變量標識插入html的方法很多。其中一種是使用類似html的標籤;另一種是使用特殊標識,如Velocity或者JDynamiTe;第三種是擴展html標籤,如tapestry。採用何種方式有著很多考慮,一個比較常見的考慮是「所見即所得」的要求。
2. 條件控制。這是一個很棘手的問題。一個簡單的例子是某物流陪送系統中,物品數低於一定值的要高亮顯示。不過對於一個具體複雜顯示邏輯的情況,條件控制似乎不可避免。當你把類似於<IF condition=」$count <= 40」><then><span class=」highlight」>count </span></then></IF>引入,就像我們當初在ASP和PHP中所做得一樣,我們將不得不再一次面對scriptlet嵌入網頁所遇到的問題。我相信你和我一樣並不認為這是一個好得的編寫方式。實際上並非所有的模板技術都使用條件控制,很多已有的應用如PHP上中的以及我曾見過一個基於ASP.NET的應用,當然還有Java的JDynamiTe。這樣網頁上沒有任何邏輯,不過這樣做的代價是把高亮顯示的選擇控制移交給編程代碼。你必需做個選擇。也許你也像我一樣既不想在網頁中使用條件控制,也不想在代碼中寫html標記,但是這個顯示邏輯是無可逃避的(如果你不想被你的老闆抄魷魚的話),一個可行的方法是用CSS,在編程代碼中決定採用哪個css樣式。特別是CSS2技術,其selector機制,可以根據html類型甚至是element的attributes來apply不同的樣式。
3. 迭代(循環)。在網頁上顯示一個數據表單是一個很基本的要求,使用集合標籤將不可避免,不過幸運的是,它通常很簡單,而且夠用。特別值得一提的是PHP的模板技術和JDynamiTe技術利用html的註釋標籤很簡單的實現了它,又保持了「所見既所得」的特性。

下面是一些技術的比較:
Velocity
 
變量定義:用$標誌
表達式語句:以#開始
強控制語言:變量賦值:#set $this = "Velocity"
     外部引用:#include ( $1 )
     條件控制:#if …. #end
非兼容語言
 
JDynamiTe
變量定義:用{}包裝
表達式語句:寫在註釋格式(<!--  □)中
弱控制語言
兼容語言
 
XSLT
變量定義:xml標籤
表達式:xsl標籤
強控制語言:外部引用:import,include
     條件控制:if,  choose…when…otherwise
非兼容語言
 
Tapestry
採用component的形式開發。
變量定義(組件定義):在html標籤中加上jwcid
表達式語句:ognl規範
兼容語言
 
 
2.模板文件:
模板文件指包含了模板語言的文本文件。
模板文件由於其模板語言的兼容性導致不同結果。與HTML兼容性的模板文件只是一個資源文件,其具有良好的復用性和維護性。例如JDynamiTe的模板文件不但可以在不同的項目中復用,甚至可以和PHP程序的模板文件互用。而如velocity的非兼容模板文件,由於其事實上是一個腳本程序,復用性和可維護性大大降低。
3.擁有動態數據的數據對像:

模板文件包含的是靜態內容,那麼其所需的動態數據就需要另外提供。根據提供數據方式的不同可以分為3種:
1.Map:利用key/value來定位。這個是最常見的技術。如velocity的VelocityContext就是包含了map對象。
Example.vm:
Hello from $name in the $project project.
 
Example.java:
VelocityContext context = new VelocityContext();
context.put("name", "Velocity");
context.put("project", "Jakarta");
 
 
2.DOM:直接操作DOM數據對象,如XSLT利用XPath技術。
3.POJO:直接利用反射取得DTO對象,利用JavaBean機制取得數據。如Tapestry。
4.模板引擎:
模板引擎的工作分為三步:
1. 取得模板文件並確認其中的模板語言符合規範。
比如velocity,確定#if有對應得#end等。Xml+xslt的模型中,xml文件標籤是否完整等。在完成這些工作後,模板引擎通常會把模板文件解析成一顆節點樹(包含模板文件的靜態內容節點和模板引擎所定義的特殊節點)。
2. 取得數據對象。
 該數據對像一般通過程序傳遞引用實現。現有的大量框架在程序底層完成,處理方式也各自不同,有兩種技術分別為推技術和拉技術。推技術:controller調用set方法把動態數據注入,模板引擎通過get方法獲得,典型代表:Struts;拉技術:模板引擎根據配置信息,找到與view對應的model,調用model的get方法取得數據,典型代表:Tapestry。
3. 合併模板文件(靜態內容)和數據對像(動態內容),並生成最終頁面。

 合併的機制一般如下,模板引擎遍歷這顆節點樹的每一個節點,並render該節點,遇到靜態內容節點按正常輸入,遇到特殊節點就從數據對像中去得對應值,並執行其表達式語句(如果有的話)。
以下詳細說明:
Velocity
 
Template template = Velocity.getTemplate("test.wm");
Context context = new VelocityContext();
context.put("foo", "bar");
context.put("customer", new Customer());
template.merge(context, writer);
當調用Velocity.getTemplate 方法時,將調用ResourceManger的對應方法。
ResourceManger先查看該模板文件是否在cache中,如果沒有就去獲取,生成resource對象並調用process()方法,確定該模板是否有效,如果有效,則在內存中生成一個Node樹。
當調用template.merge()時,遍歷這顆Node樹,並調用每個Node的render方法。對於模板中的變量和對像Node,還將調用execute()方法,從context中取得value。
   註:ResourceManger在runtime esource包下,Node在runtimeparser ode包下
 
Tapestry
 
Tapestry比較麻煩,先介紹一下http請求的處理過程。
當httprequest請求到達時。該請求被ApplicationServlet捕獲,隨後ApplicationServlet通過getEngine取到對應的Engine,通過該engine的getService拿到對應的service,調用其service方法執行http請求。

每個service通過RequestCycle對象的getPage方法取得Page對象,並將其設置為該Cycle對象的active Page。之後service調用renderResponse方法執行輸出。
renderResponse調用page的getResponseWriter(output)取得writer對象,並把它傳給cycle.renderPage(writer)方法,該方法調用page的renderPage方法。
Page執行renderPage時,首先判斷是否有listener的請求,如果有則處理listener請求;然後調用BaseComponentTemplateLoader的process方法把模板文件載入並形成一個component節點樹,依次執行節點的renderComponent方法。
每個component對像將通過ongl的機制取得對像屬性。並把該值寫入輸入流。
例如:insert component
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
 if (cycle.isRewinding())
     return;
 Object value = getValue();
 if (value == null)
     return;
 String insert = null;
 Format format = getFormat();
 if (format == null) {
     insert = value.toString();
 }
 else{

     try{
  insert = format.format(value);
     }
     catch (Exception ex) {
  throw new ApplicationRuntimeException(
Tapestry.format("Insert.unable-to-format",value),this, getFormatBinding().getLocation(), ex);
     }
 }
 String styleClass = getStyleClass();
 if (styleClass != null) {
     writer.begin("span");
     writer.attribute("class", styleClass);
     renderInformalParameters(writer, cycle);
 }
 if (getRaw())
     writer.printRaw(insert);
 else
     writer.print(insert);
 if (styleClass != null)
     writer.end(); // <span>

    }
getValue為取得insert的value屬性。
 
 
三、JSP技術分析
1. JSP技術:
JSP,一個偽裝後的servlet。web server會對任何一個jsp都生成一個對應jsp類,打開這個類,就會發現,jsp提供的是一個代碼生成機制,把jsp文件中所有的scriptlet原封不動的copy的到生成的jsp類中,同時調用println把所有的html標籤輸出。
Test.jsp:
<html>
<head><title>jsp test</title></head>
<body>
<table >
   <tr><td><font face="Arial" size="2" color="#000066">
      <b class="headlinebold">The jsp test file</b>
</tr></td> </font>  
</table>
<body>
</html>
 
Test_jsp.java:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;

 
public class Test _jsp extends HttpJspBase {
  private static java.util.Vector _jspx_includes;
  public java.util.List getIncludes() {
    return _jspx_includes;
  }
  public void _jspService(HttpServletRequest request, HttpServletResponse response)
 throws java.io.IOException, ServletException {
    JspFactory _jspxFactory = null;
    javax.servlet.jsp.PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;
    JspWriter _jspx_out = null;
 
    try {

      _jspxFactory = JspFactory.getDefaultFactory();
      response.setContentType("text/html;charset=ISO-8859-1");
      pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true);
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
 
      out.write("<html> ");
      out.write("<head><title>jsp test</title></head> ");
      out.write("<body> ");

      out.write("<table );
      out.write("<tr><td><font face="Arial " size="2" color="#000066">       ");
      out.write("<b class="headlinebold">The jsp test file");
      out.write("</b>       ");
      out.write("</tr></td></font> ");
      out.write("</table> ");
      out.write("<body> ");
      out.write("</html>");
    } catch (Throwable t) {
      out = _jspx_out;
      if (out != null && out.getBufferSize() != 0)
 out.clearBuffer();

      if (pageContext != null) pageContext.handlePageException(t);
    } finally {
      if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
    }
  }
}
 
 
2. Taglib技術:
Taglib作為jsp之上的輔助技術,其工作本質依托與jsp技術,也是自定義標籤翻譯成java代碼,不過這次和jsp略有不同,它還要經過幾個過程。
先來看一下,實現一個tag的2個要點:
1. 提供屬性的set方法,此後這個屬性就可以在jsp頁面設置。以jstl標籤為例 c:out value=""/,這個value就是jsp數據到tag之間的入口。所以tag裡面必須有一個setValue方法,具體的屬性可以不叫value。例如setValue(String data){this.data = data;}。這個「value」的名稱是在tld裡定義的。取什麼名字都可以,只需tag裡提供相應的set方法即可。
2. 處理 doStartTag 或 doEndTag 。這兩個方法是 TagSupport提供的。還是以c:out value=""/為例,當jsp解析這個標籤的時候,在「<」處觸發 doStartTag 事件,在「>」時觸發 doEndTag 事件。通常在 doStartTag 裡進行邏輯操作,在 doEndTag 裡控制輸出。
  在處理tag的時候:
  0. 從tagPool中取得對應tag。
1.     為該tag設置頁面上下文。

2.     為該tag設置其父tag,如果沒有就為null。
3.     調用setter方法傳入標籤屬性值tag,如果該標籤沒有屬性,此步跳過。
4.     調用doStartTag方法,取的返回值。
5.     如果該標籤有body,根據doStartTag返回值確定是否pop該標籤內容。如果要pop其body,則:setBodyContent(),在之後,doInitBody()。如果該標籤沒有body,此步跳過。
6.     調用doEndTag()以確定是否跳過頁面剩下部分。
7.     最後把tag類返還給tagPool。
tag類為:
package my.customtags;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
 
public class Hidden extends TagSupport{
    String name;  
    public Hidden(){    name = "";    }
    public void setName(String name){    this.name = name;    }

    public void release(){   value = null;    }
    public int doStartTag(){  return EVAL_BODY_INCLUDE;}
public int doEndTag() throws JspTagException{
   try{   pageContext.getOut().write(", you are welcome");   }
   catch(IOException ex){   throw new JspTagException("Error!");    }
   return EVAL_PAGE;
}
}
 
Jsp頁面:
<my:hidden name="testname"/>
 
生成的jsp代碼:
my.customtags.Hidden _jspx_th_my_hidden_11 = (my.customtags.Hidden) _jspx_tagPool_my_hidden_name.get(my.customtags.Hidden.class);
_jspx_th_my_hidden_11.setPageContext(pageContext);
_jspx_th_my_hidden_11.setParent(null);
_jspx_th_my_hidden_11.setName("testname");

int _jspx_eval_my_hidden_11 = _jspx_th_my_hidden_11.doStartTag();
if (_jspx_th_my_hidden_11.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE)
   return true;
_jspx_tagPool_my_hidden_name.reuse(_jspx_th_my_hidden_11);
return false;
 
 
   Taglib技術提供兩個機制,Body和non-Body導致了taglib的出現了兩個分支:Display Tag和Control Tag, 前者在java code中嵌入了html標籤,相當與一個web component,而後者則是另一種模板腳本。
 
四、兩種技術方案的比較:
1. 技術學習難易度
模板技術。使用模板技術,第一點就是必須學習模板語言,尤其是強控制的模板語言。於是模板語言本身的友好性變的尤為重要。以下依據友好性,表現力以及復用性三點為主基點比較了一下幾種模板技術。
Velocity:
Turbine項目( http://jakarta.apache.org/Turbine )採用了velocity技術。
1.友好性不夠。理由: 強控制類型,出現頁面顯示控制代碼和html混合。與Html的不兼容,無法所見即所得。遇到大的HTML頁面,從一個 「#if」找到對應的 「#end」也是很痛苦的一件事情。
2.表現力強。理由:強控制語言。
3.復用性弱。理由:模板腳本和頁面代碼混合。
 
XSLT
Cocoon項目(
http://cocoon.apache.org/ )採用XML + XSLT的方法。CSDN社區也是採用此方案。
1.內容和顯示風格分離,這點XSLT做的最好。
2.速度慢。理由:XSLT的使用XPath,由於是要解析DOM樹,當XML文件大時,速度很慢。
3.友好性不夠。理由:由於沒有HTML文件,根本看不到頁面結構、顯示風格和內容。XSL語法比較難以掌握,由於沒有「所見即所得」編輯工具,學習成本高。
4.表現力強。理由:強控制語言。
5.復用性弱。理由:xsl標籤和html標籤混合。
 
JDynamiTe
1.表現力中等。理由:弱控制語言。
2.友好性強。理由:所見即所得的效果。在模板件中的ignore block在編輯條件下可展示頁面效果,而在運行中不會被輸出。
3.復用性強。理由:利用html標籤。
 
Tapestry
1.友好性中等。理由:整個Tapestry頁面文件都是HTML元素。但是由於component會重寫html標籤,其顯示的樣子是否正確,將不預測。
2.表現力強。理由:強控制語言。
3.復用性強。理由:擴展了HTML元素的定義。
 
 
 
在JSP中大量的使用TagLib,能夠使得JSP的頁面結構良好,更符合XML格式,而且能夠重用一些頁面元素。但TagLib的編譯之後的代碼龐大而雜亂。TabLib很不靈活,能完成的事情很有限。TabLib代碼本身的可重用性受到TagSupport定義的限制,不是很好。 另外是,我不得不承認的一件事是,TagLib的編寫本身不是一件愉快的事情,事實我個人很反對這種開發方式。
 
2. 技術使用難易度

模板技術:模板技術本身脫離了Web環境,可以在不啟動Web server得情況下進行開發和測試,一旦出錯詳細的信息易於錯誤的定位。由於模板引擎的控制,頁面中將只處理顯示邏輯(儘管其可能很複雜)
JSP技術:工作在Web環境下,開發測試一定要運行web server。此外,一些TagLib能夠產生新的標籤,頁面的最終佈局也必須在web環境下才可以確定。測試時出錯信息不明確,特別是TagLib得存在,極不容易定位。由於其本質是程序,很容易在其中寫入業務邏輯,甚至於數據庫連接代碼,造成解耦的不徹底。
 
3. 總結
模板技術更加專注於頁面的顯示邏輯,有效幫助開發人員分離視圖和控制器。在學習,開發和測試都更加容易。
JSP技術本身是一個早期的技術,本身並沒有提出足夠的方式來分離視圖和控制器。相反,我認為其本身是鼓勵開發人員不做解
相關鏈接
 

原文出自:Template和JSP技術
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]困擾JSP的一些問題與解決方法
困擾JSP的一些問題與解決方法

  如今每一個使用servlets的開發者都知道JSP,一種由Sun公司發明並花費大量精力加以推行並建構在servlet技術之上的web技術。JSP將servlet中的html代碼脫離了出來,從而可以加速web應用開發和頁面維護。實際上,由Sun發佈的官方"應用開發模型"文檔上說得更遠: "JSP技術應該被視為標準,而servlets在多數情況下可視為一種補充。" ( Section 1.9, 1999/12/15聽取意見版 )。

  本文的目的在於聽取對該申明的合理性的評估 -- 通過比較JSP和另一項基於servlets的技術: template engines(模板引擎)。

  直接使用Servlets的問題

  起初,servlets被發明,整個世界都看到了它的優越。基於servlet的動態網頁可以被快速執行,可以在多個服務器之間輕易轉移, 並且可以和後台數據庫完美地集成。 Servlets被廣泛接受成為一種web服務器端的首選平台。
但是,通常通過簡單方式即可實現的html代碼現在卻要讓程序員通過 out.println()調用每一行HTML行,這在實際的 servlet應用中成為了一個嚴重問題。 HTML內容不得不通過代碼來實現, 對於大的HTML頁來說不啻是一項繁重費時的工作。另外,負責網頁內容的人員不得不請開發人員來進行所有的更新。為此,人們尋求這一種更好的解決方式。

  JSP到!

  JSP 0.90出現了。在這種技術中你可以將Java代碼嵌入到HTML文件,服務器將自動為頁面創建一個 servlet。 JSP被認為是一種寫servlet的簡易方式。所有HTML可以直接得到而不必通過out.println()調用,而負責頁面內容的人員可以直接修改HTML而不必冒破壞Java代碼的風險。
  但是,讓頁面美術設計師和開發人員在同一文件上工作並不理想,讓Java嵌入HTML被證明是就像將HTML 嵌入Java一樣令人尷尬。讀取一堆很亂的代碼仍然是一件困難的事情。


  於是,人們在使用jsp方面變得成熟,更多地使用了JavaBeans。 Beans包含了jsp所需的業務邏緝代碼。JSP中的大多數代碼都可以取出來放到bean中去,而只留下極少的標記用於調用bean。

  最近,人們開始認為這種方式下的JSP頁面真的很像是視圖(view)。它們成為一個用於顯示客戶端請求的結果的組件。於是人們會想,為什麼不直接對view發送請求呢? 目標view如果對該請求不合適又將如何? 說到底,很多的請求有多種可能來取得結果view視圖。例如,同一請求可能產生成功的頁面,數據庫例外出錯報告,或者是缺少參數的出錯報告。同一請求可能產生一個英文頁面也可能是西班牙文頁面,這取決於客戶端的locale。為什麼客戶端必須直接將請求發送給view?為什麼客戶端不應該將請求發送給一些通用的服務器組件並讓服務器來決定JSP view的返回?

  這使很多人接受了已被稱為"Model 2"的設計, 這是在JSP 0.92中定義的基於model-view-controller的模型。在這種設計中,請求被發送到一個servlet控制器,它執行了商業邏緝並產生一個相近的數據"model"來用於顯示。這一數據隨後通過內部送到一個JSP "view"來進行顯示,這樣看起來JSP頁就像是一個普通的嵌入的JavaBean。 可以根據負責控制的servlet的內部邏輯來選擇適當的JSP頁面進行顯示。這樣,JSP文件成為了一個漂亮的template view。這就是另一種發展,並被另外一些開發者所推崇至今.

  進入Template Engines

  使用template engine來代替通常目的的JSP, 接下去的設計將變得簡單,語法更簡單,出錯信息更易讀,工具也更用戶化。 一些公司已經做了這樣的引擎,最著名的可能是WebMacro ( http://webmacro.org, from Semiotek),他們的引擎是免費的。
  開發者應該明瞭,選定一個template engine來取代JSP提供了這麼一些技術優勢,這也正是jsp的一些不足之處:

  問題 #1: Java代碼太模板化了


  雖然被認為是不好的設計,JSP仍試圖將Java代碼加入web頁面。這有些像是Java曾經做的,即對C++的簡化修改,template engines也通過將jsp中的較低層的源碼移去來使之簡化。Template engines實行了更好的設計。

  問題 #2: 要求Java代碼

  在JSP頁中要求寫一些Java代碼。例如,假設某頁要決定當前web應用中根的上下文從而導向其主頁,
在JSP中最好使用如下Java代碼:

  <a href="<%= request.getContextPath() %>/index.html">Home page</a>
 
  你可以試圖避免 Java代碼,而使用 <jsp:getProperty> 標記但這將給你六下難以閱讀的字串:

  <a href="<jsp:getProperty name="request"
  property="contextPath"/>/index.html">HomePage</a>

  使用template engine則沒有Java代碼和難看的語法。這裡是同樣要求下在WebMacro中的寫法:

  <a href="$Request.ContextPath;/index.html">Home page</a>

  在WebMacro中, ContextPath 作為 $Request變量的一個屬性,使用類似Perl的語法。其它er template engines使用了其它的語法類型。
  
  再看另 一個例子,假設一個高級的"view"需要設定一個cookie來記錄用戶缺省的顏色配置 -- 這種任務看起來大概只能由view而不是servlet控制器來完成。在JSP中要有這樣的Java代碼:

  <% Cookie c = new Cookie("colorscheme", "blue"); response.addCookie(c); %>

  在WebMacro中則沒有Java代碼:

  #set $Cookie.colorscheme = "blue"


  作為最後一個離子,假如又要重新找回原來的cookie中的顏色配置。對於JSP,我們可以認為也有一個相應的工具類來提供幫助,因為用getCookies()直接做這樣低層的會變得可笑而且困難。在JSP中:

  <% String colorscheme = ServletUtils.getCookie(request, "colorscheme"); %>

  在WebMacro中沒有對工具類的需要,通常是:$Cookie.colorscheme.Value .對寫jsp的圖形藝術師,又是哪一種語法更容易學習呢?

  JSP 1.1 引入了自定義標記(custom tags)允許任意的和HTML相似的標記在JSP頁面中在後台執行Java代碼,這將具有一定的價值,但前提是要有一個廣泛知曉的,全功能的,可以免費得到的,標準化的標記庫。目前還沒有出現這樣的標記庫。

  問題 #3: 簡單工作仍然很累人

  即使是很簡單的工作,例如包含 header和 footer,在JSP中仍然很很困難。 假設有一個 "header"和一個 "footer"模板要包含到所有頁面,而每一個模板要在content中包含當前的頁標題。
在JSP中最佳辦法是:
  <% String title = "The Page Title"; %>
  <%@ include file="/header.jsp" %>
  ...你的頁面內容...
  <%@ include file="/footer.jsp" %>

  頁面設計者要記住不能遺漏第一行的分號並要將title定義為一個字符串。此外, /header.jsp和/footer.jsp必須在根目錄下並且必須是可存取的完整文件。
  在WebMacro中包含headers和footers做起來比較簡單:

  #set $title = "The Page Title"
  #parse "header.wm"
  Your content here
  #parse "footer.wm"

  這裡對設計者來說沒有要牢記的分號或對title的定義, .wm文件可以放在可自定義的搜索路徑下。


  問題 #4: 很粗的循環

  在JSP中循環很困難。這裡是用JSP重複打印出每一個ISP對像名字。
  <%
  Enumeration e = list.elements();
  while (e.hasMoreElements()) {
  out.print("The next name is ");
  out.println(((ISP)e.nextElement()).getName());
  out.print("<br>");
  }
  %>

  也許什麼時候會有用戶自定義標記來做這些循環。對"if"也是如此。JSP頁可能看上去成了很古怪的java代碼。而同時,webmacro循環很漂亮:
  #foreach $isp in $isps {
  The next name is $isp.Name <br>
  }

  如果必要的話,#foreach指令可被自定義的 #foreach-backwards指令很容易地取代。

  用jsp的話很可能變這樣:(這裡是一個可能的 <foreach>標記)

  <foreach item="isp" list="isps">
  The next name is <jsp:getProperty name="isp" property="name"/> <br>
  </foreach>

  設計者當然地回選擇前者。
  問題 #5: 無用的出錯信息

  JSP常有一些令人驚訝的出錯信息。這是因為頁面首先被轉換成為一個servlet然後才進行編譯。好的JSP 工具可以相對增加找到出錯位置的可能性,但即使是最好的工具也無法使所有出錯信息都能容易地被讀懂。由於轉化的過程,一些錯誤對工具來說可能根本不可能被識別。
例如,假設JSP頁面需要建立一個對所有頁通用的標題。以下代碼並沒有錯:

  <% static String title = "Global title"; %>

  但Tomcat會提供以下出錯信息:

  work/%3A8080%2F/JC_0002ejspJC_jsp_1.java:70: Statement expected.
  static int count = 0;
  ^

  此信息認為以上腳本被放入 _jspService()方法而靜態變量不允許放入方法中。該語法應該是 <%! %>。頁面設計者很難讀懂這些出錯信息。即使最好的平台在這方面也做得很不夠。即使所有 Java代碼都從頁中移出也無法解決問題。另外,以下表達式有什麼錯?

  <% count %>
  tomcat給出:
  work/8080/_0002ftest_0002ejsptest_jsp_0.java:56: Class count not found in
  type declaration.
  count
  ^
  work/8080/_0002ftest_0002ejsptest_jsp_0.java:59: Invalid declaration.
  out.write("\r\n");
  ^

  換句話說,只是遺失了一個標記而已。應該是 <%= count %>。

  由於template engine可以在template文件中直接產生而沒有任何戲劇性的向代碼轉化,所以可以非常容易地給出適當的出錯報告。 依次類推,當c語言的命令被打入Unix shell的命令行, 你並不希望shell 會生成一個C程序來運行這個命令,而只是需要shell簡單地解釋命令並加以執行,如有錯誤也直接給出。

  問題 #6: 需要一個編譯器

  JSP需要一個置放在webserver中的編譯器。由於Sun拒絕放棄包含了他們的javac編譯器的tools.jar庫, 這其中就變得有問題了。Web服務器可以包含進一個第三方的編譯器如ibm的 jikes。但這樣的編譯器並不能在所有平台上順利工作(用 C++寫成的) 也不利於建立純Java 的web服務器。 JSP有一個預編譯選項可以起到一定作用,儘管並不完美。

  問題 #7: 空間的浪費


  JSP消耗了額外的內存和硬盤空間。對服務器上每30K的JSP文件,必須要有相應的大於30K的類文件產生。實際上使得硬盤空間加倍。考慮到JSP文件隨時可以很容易地通過 <%@ include>包含一個大的數據文件,這樣的關注有著很現實的意義。同時,每一個JSP的類文件數據必須加載到服務器的內存中,這意味著服務器的內存必須永遠地將整個JSP文檔樹保存下去。少數一些JVM有能力將類文件數據從內存中移去;但是,程序員通常無法控制這樣的規則來重新申明,而且對大的站點來說重新申明可能不是很有效。對template engines由於沒有產生第二個文件,所以節省了空間。Template engines還為程序員提供對templates在內存中進行緩存的完全控制。

  使用template engine也有一些問題:

  Template的問題 #1: 沒有嚴格定義

  template engine該如何工作並沒有嚴格定義。可是,但相對jsp來說,其實這並不很重要,和 JSP不同的是,template engines對web服務器沒有任何特殊要求 -- 任何支持servlet的服務器都可以支持template engines (包括API 2.0服務器如Apache/JServ,它們並不能完全支持 JSP)! 如果為最好的template engine設計提供健康的競爭本可以引起一場耀眼的革新,特別是有開放源碼的促進,(可以讓思想相互推動和促進),那麼今天的WebMacro就會像Perl一樣,沒有嚴格定義但公開源碼組織的推動就是它的標準。

  Template的問題 #2: 沒有獲得公認

  Template engines並未被廣泛知曉。JSP已經佔據了極大的商業市場,並且深入人心。而使用g template engines只能是一種未被瞭解的替代技術。

  Template的問題 #3: 尚未調配好

  Template engines還沒有被高度地調配好。沒有對template engine 和JSP兩者進行性能測試和比較。理論上說一個調配完好的template engine實現應該和一個調配好的JSP相匹配;但是,考慮到第三方為jsp已經作出了這麼深遠的推動,結果只有jsp被很好地調配好了。


  JSP的角色

  當然地,JSP在將來必然會有其地位。即使從名稱上也可以看出JSP和ASP的相似性,它們只有一個字母的差別。所以如果要讓使用asp的人們轉向java,非常相似的jsp環境將對此起到很大的推動作用,和asp保持這種對應關係所能起到的作用應該也是被推出jsp的設計者重點考慮到的。
然而這裡想要強調的一點是:有利於轉入新環境的工作者,以及實際上是否是使用該環境的最佳方式,這兩者是有很大不同的。

  JSP日益顯示出它正成為最重要的java技術之一, 它讓人們離開ASP的世界 -- 由此,Sun將支持這一強有力的商業case, Java相關技術支持者也將給予更大力的支持。

  可是,這並非java平台的最佳解決方案。這將使java解決方案變得好像是沒有java的解決方案了。

原文出處:網絡文摘,困擾JSP的一些問題與解決方法
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]移植到Velocity(一)

Migrating to Velocity 移植到Velocity (翻譯自 Migrating to Velocity 刊登於 www.onjava.com )

by Jason R. Briggs
translated by 20is87062@cis.nctu.edu.tw">Chen-en Lu (陸振恩)
04/14/2004

在動態網頁的產生有很多的選擇。一般來講,大部分的專案會使用一些公訂的標準,所以如果平台選擇了Java,那可能使用的就是JSP。以很多角度來看這種選擇是合理的:新進員工可能對一個標準比較有經驗;很多的網頁伺服器有支援JSP,這讓Web平台有較多的選擇;可取得的文件也比較多,因為很多東西別人都做過了。然而,有時候使用標準並不一定是最佳的選擇。也許您希望擁有較多的空間去控制文件的產生;也許無法接受JSP的效率問題;也許公司正在推一個新的技術方向;諸如此類的。Velocity是一個簡單有效率且功能強大的樣板引擎(Template Engine),可以用來取代目前Java中動態網頁產生的標準JSP。某些方面 它提供優於JSP的執行效能,並且它也帶給網頁開發者一個新的分離程式碼跟內容的的全新思維。

唯一的不利因素是如果已經存在了一個JSP-based的專案,而且又有較沒有經驗的團隊且已經對JSP有所接觸,移植到Velocity本身就有所挑戰。在這邊篇文章中,我試著移除這個障礙,用些較簡單的Velocity延伸,來降低切換到Velocity所帶來的痛苦。

一些背景


大約兩年前,我接手了由北歐的公司所寫的一個一般大小,Servlet-based的系統 (並不是所有北歐國家所寫的都有linux的品質)。這系統是用很基本的樣板引擎 (template engine),但也有很多的html嵌在文件中。事實上裡面有一大長串的不良設計,還有很多噁心的程式碼在其中 (包括了"一手包辦"的巨型method的寫作方法),這些糾結的程式碼絕對會讓接手的程式員做惡夢。

考量到原有系統的限制,還有公司希望有更完整的需求。我下定決心廢棄掉99%的程式碼,重新寫過,從這個潦草的專案中重新設計了一個新的J2EE系統。新系統現在已經跑了幾乎一年了,並且只有少少的零星狀況。這不只大大增加了維護性,工作也變得快樂許多。

最近,即使這不是很主流的觀點,但我發現我變的不能滿足原本用JSP去實作Web及WAP的網頁的方法。原本的設計是希望越接近MVC pattern越好,但Java程式碼嵌在網頁中並不總是能那麼容易達到這個需求。這個工作在使用JSTL (Java standard tag library)之後好像比較容易些。但必然地,我們會發現有些事情光靠JSTL是無法滿足的,所以可能會寫一個屬於自己的Taglib去達到我們想要的需求。然後卻發現有一些物件您需要在頁面中 直接使用但卻不能用,除非用一些java程式碼把我們的taglib包裝起來。或更常見的,因為要呼叫一些方法,但由於時間的限制,就直接的使用java程式碼去呼叫,只求功能上是可以達到最後要求就好了,但絕對不如剛開始開發時所想的那麼優雅。

接受挑戰

重新評估網頁端的技術變的越來越重要了,因為Servlet Container在處理 JSP是以一種較費時的方式(鮮為人知的"開太多檔"的問題),在評估了數個選擇之後,因為以下幾個因素我決定使用Velocity:

  1. 公開程式碼 — 不管這對其他人來講是不是重要的考量,但是我主要是做linux的業務,所以開放原始碼會是我的首要考量。
  2. 效能 — 在我對較簡單的網頁做的初步測試發現,Velocity擁有優於JSP大概35%至45%的效能。而對於較複雜的網頁,這個差距就比較縮小
    (且在加入各種因素在其中時,差距大概掉到5%左右),但這個效能上的提升還是存在的。
  3. 優雅 — Velocity的文法簡單而且實用。要加一些指令在其中(在Velocity中叫directive)也是非常的直覺簡單,完全沒有違反簡單的原則。

我也贊成Velocity的觀點:如果把一個JSP/JSTL的網頁交給網頁設計者跟Velocity比起來只會更難而不會更容易。(JSP/JSTL的缺點是已經假設網頁會是XHTML的格式的,也就是用XML當作動態網頁產生的格式,這並不會讓非技術的設計人員會有好日子過)。

如何運作

在Velocity中,每一個頁面(或是您喜歡的話,也可說是每一群頁面),就提供一個Servlet。您的Servlet提供所有這個頁面所需要的資料,把它放在一個環境(context)中,最後Velocity會根據環境使用樣板(template)把整個網頁繪製出來。而樣板會包含HTML的標籤以及Velocity的directive— 沒有 Java程式碼。

這當然就是它實用的地方,因為它嚴格的強迫我們不能使用任何的程式碼在樣板之中,也簡化了頁面 — 我個人認為這是JSP網頁無法做到的。

跟JSP做一個比較:我們使用JavaBeans來盡可能的隱藏Java程式碼。我們使用JSP的標籤 useBean ,並且在使用它們前設定對這些beans的值,再透過JSTL的標籤來取得這些值:


<jsp:useBean id="comp" scope="session"
class="com.lateralnz.comp.view.CompDefBean" />
<jsp:setProperty name="comp"
property="someprop" value="someval" />
...
...
<c:forEach var="comp" items="${comps.iterator}">
<tr>
<td class="list">
<c:out value="${comp.compCode}" />
</td>
...

可以想像的,直接把JSP轉成Velocity會帶來一定數量的苦工。若帶領的是一個較沒經驗的團隊,更不用說所連帶的學習曲線。時間永遠是一個問題,即使專案不是大到無法想像, 但我們依然還是沒有資源可以把上百個JSP的頁面轉換到另一個新技術,尤其是用一個完全不一樣的新技術。因此,我決定使用漸進的方式轉換到Velocity,採取一個比較好掌握的方法。

加進JSP沒有的功能

第一步是允許Velocity樣板可以以跟JSP相同的方法被呼叫。首先我們先產生一個基本的Servlet (VMServlet)並設定一個mapping,用"*.vm"當做url-pattern:


<servlet-mapping>
<servlet-name>vm</servlet-name>
<url-pattern>*.vm</url-pattern>
</servlet-mapping>

意思是 http://mydomain.com/mypath/test.vm 會被當作一個標準的Servlet傳給VMServlet。而Servlet的標準方法( doGet, doPost )會去呼叫 VMServlet.processRequest ,在其中會把一些隱含物件加入Velocity的context中 、讀取樣板、並產生最終的結果。


protected void processRequest(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

這裡是透過Velocity標準方法去讀取樣板的部分 (樣板會被引擎所快取):


  String templateName = request.getServletPath();
Template tmp = ve.getTemplate(templateName);
VelocityContext ctx = new VelocityContext();

Web環境的路徑、session、jsessionid,還有servlet request都會加進context之中 (這些都是在JSP中常用的物件):


  HttpSession session = request.getSession(true);
ctx.put("contextPath",
request.getContextPath());
ctx.put("session", session);
ctx.put("jsessionid", JSESSIONID_STR +
session.getId());
ctx.put("request", request);

最後一步就是繪製出結果,使用 PrintWriter ,Velocity Context ,還有template:


  response.setContentType(contentType);
PrintWriter out = response.getWriter();
tmp.merge(ctx, out);

原文出處:ONJava.com: Migrating to Velocity [Apr. 14, 2004]
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]移植到Velocity(二)

複製JSP

觀察一下基本的JSP,為了方便我們做移植`,有一些功能還是是需要的。

在下面的例子,我們設定了content type,產生一個JavaBean (一個Date物件),包含了一些JSP "片段(fragment)"檔,使用JSTL taglib中的 fmt 以呈現 ResourceBundle 中的文字。最後一行呼叫 getTime 此Date的方法(也可以很簡單地透過JSTL c:out 來取得同樣的值)


<%@ page contentType="text/html; charset=UTF-8" %>
<fmt:setBundle basename="com.lateralnz.messages" var="sysm" />
<jsp:useBean id="test" scope="session" class="java.util.Date" />
<html xmlns="http://www.w3.org/1999/xhtml">
<%@ include file="htmlhead.jsf" %>
<body>
<%@ include file="header.jsf" %>
<h1><fmt:message key="app.welcome_title" bundle="${sysm}" /></h1>
<p><fmt:message key="app.welcome_text" bundle="${sysm}" /></p>
<p><%= test.getTime() %></p>
</div>
</body>
</html>

Velocity提供了一些同樣功能的directive,但並非全部。 在標準的Velocity安裝中, Date ResourceBundle 會被放在Servlet Context之中,所以可以在樣板中直接呼叫。第二步,滿足需求(先暫時不管之前我所講的MVC的目標)。我寫了屬於自己的 #usebean 的directive(參照 UseBeanDirective.java 的程式碼)。它傳進name、使用的class name、還有它的scope。 UseBeanDirective 則會產生JavaBean且把它放在context之中。如果scope是"application",它會被放在static HashMap 供之後使用;如果scope是"session",則它會被放在使用者的session之中。

VMServlet 中,同樣需要一個method可以在template中如JSP中同樣的方法去設定content type。我目前的選擇是快取content type,以requesrtURI當作Key,並使用Velocity樣板的 getData 方法來找此頁中( $contentType )這個變數:


private final String determineContentType(
Node node) {
for (int i = 0;
i < node.jjtGetNumChildren();
i++) {
StringBuffer sb = new StringBuffer();
Node n = node.jjtGetChild(i);
if (n instanceof org.apache.velocity.
runtime.parser.node.ASTSetDirective) {
Token t =
findTokenWithPrefix(n.getFirstToken(),
"$");
if (t == null ||
!t.toString().equalsIgnoreCase(
"$contentType")) {
continue;
}
else {
t = findTokenWithPrefix(t, "\"");
String ct = StringUtils.remove(
t.toString(), "\"");
return ct;
}
}
}
return "text/html";
}
private final Token findTokenWithPrefix(
Token t,
String prefix) {
if (t == null) {
return null;
}
else if (t.toString().startsWith(prefix)) {
return t;
}
else {
return findTokenWithPrefix(t.next, prefix);
}
}

雖然很明顯地,這是竄改原始碼,但是也達到了目的。因此,我加了以下的程式碼到 processRequest:


String contentType;
if (contentTypes.containsKey(requestURI)) {
contentType =
(String)contentTypes.get(requestURI);
}
else {
contentType =
determineContentType((Node)tmp.getData());
contentTypes.put(requestURI, contentType);
}

最終的Velocity樣板如下面所示:


#set ( $contentType = "text/html" )
<html xmlns="http://www.w3.org/1999/xhtml">
#usebean ( test "java.util.Date" page )
#usebean ( sysm
"com.lateralnz.MyMessagesBundle" application )
#parse ( "htmlhead.vm" )
<body>
#include ( "header.vm" )
<h1>
$sysm.getString('app.welcome_title')
</h1>
<p>
$sysm.getString('app.welcome_text')
</p>
<p>$test.getTime()</p>
</body>
</html>

如您所見,跟原本的JSP頁面沒有太大的改變。而程式碼中 ResourceBundle 是透過一個JavaBean的方式讀進來,但在真正的系統下,我把它快取在 VMServlet 中,並且自動地當作隱含物件讀進來中。

還有一小塊我們還沒有提到。如果使用JSTL可能還會用到一些Jakarta taglibs。如果是這個情況的話,則還需要一些程式碼。幸好,透過產生新的directive去延伸Velocity是很容易的事情 (事實上我發現比寫taglib還簡單,除了API相對少以外)。到目前為止,我只需要產生一些額外的directive:一個是產生格式化的日期,一個是格式化的數字,還有一些簡單 的directive去終止繪製一個樣板(以貼在Velocity mailing list中的一個小技巧為基礎):


public class ExitDirective extends Directive {
private static final String EXIT = "exit";
private static final
StopRenderingException STOP_RENDER_EXCEPTION =
new StopRenderingException();
public boolean render(
InternalContextAdapter context,
Writer writer,
Node node)
throws IOException,
ResourceNotFoundException,
ParseErrorException,
MethodInvocationException {
throw STOP_RENDER_EXCEPTION;
}
public int getType() {
return DirectiveConstants.LINE;
}
public String getName() {
return EXIT;
}
}

VMServlet processRequest 方法中,會捕捉到 StopRenderingException ,並且有效地終止樣板繪製。在我們的系統中,我們用這個特別的directive在WAP header中去檔掉沒有登入的使用者:


#if ( !$session.getAttribute(USER_ID) )
<card id="message" title="Invalid Session">
<p>Sorry, but your session has
timed out.</p>
</card>
</wml>
#exit ( )
#end

總結

Velocity提供Web開發者一個強大的樣板機制—然而,也可能對想要從JSP和taglibs移植的人剛開始就帶來不算小的障礙。使用簡單的servlet,還有採用directive去延伸Velocity,大家可以效法這 些方來完成大部分JSP的功能。也因此讓移植的工作可以以小規模、低風險的方式一步一步地邁進。

相關鏈結

最後一點:如果您是*nix的開發者,絕對不要忘記 sed 這個好用的工具,它可以幫您做掉許多移植JSP時所需面對的苦工。我並不是sed的專家,但也只花了我一點時間就馬上轉換了90%的程式碼。

Jason R. Briggs is a technical architect and developer for a wireless technology company

原文出處:ONJava.com: Migrating to Velocity [Apr. 14, 2004]
冷日
(冷日)
Webmaster
  • 註冊日: 2008/2/19
  • 來自:
  • 發表數: 15771
[轉貼]Spring Gossip: 以 Tiles 為例

Spring Gossip: 以 Tiles 為例

Tiles 是一個模版引擎(Template Engine),它可以讓網頁的配置(Layout)(像是使用<table>標籤)與實際要呈現資料的內容分離,Tiles提供一個版面管 理機制,您可以在Spring的View層使用Tiles來管理網頁的版面配置,這有兩個基本動作必須設定,一是設定 org.springframework.web.servlet.view.tiles.TilesView,二是設定 org.springframework.web.servlet.view.tiles.TilesConfigurer,例如:
  • mvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="viewResolver"
class="org.springframework.web.servlet.
→ view.InternalResourceViewResolver">
<property name="viewClass">
<value>
org.springframework.web.servlet.view.tiles.TilesView
</value>
</property>
</bean>

<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.
→ tiles.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles-defs.xml</value>
</list>
</property>
</bean>

<bean name="/welcome.do"
class="org.springframework.web.servlet.
→ mvc.ParameterizableViewController">
<property name="viewName">
<value>welcome</value>
</property>
</bean>
</beans>


注意到設定TilesView之後,您傳回的ModelAndView中設定的"view"名稱並不是指向真正的資源URL位置,而是在Tiles定義檔 中所定義的名稱,以上面的設定為例,ParameterizableViewController的"viewName"設定為"welcome",則它 會到Tiles設定檔中找相關的"welcome"設定,Tiles定義檔的讀取位置可以由TilesConfigurer的"definitions" 屬性來設定,假設您的Tiles定義檔是這麼設定的:
  • tiles-defs.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">

<tiles-definitions>
<definition name=".myLayout"
path="/WEB-INF/jsp/tiles/myLayout.jsp">
<put name="title" value="Sample Page Title"/>
<put name="header" value="/WEB-INF/jsp/tiles/header.jsp"/>
<put name="menu" value="/WEB-INF/jsp/tiles/menu.jsp"/>
<put name="footer" value="/WEB-INF/jsp/tiles/footer.jsp"/>
<put name="body" value="/WEB-INF/jsp/tiles/body.jsp"/>
</definition>

<definition name="welcome" extends=".myLayout">
<put name="title" value="Tiles 測試頁面"/>
</definition>
</tiles-definitions>


在定義檔中,首先定義一個".myLayout",名稱開頭有個逗點在命名慣例上通常表示這是一個版面定義,而不是一個真正要呈現畫面資料的定義, "welcome"定義可以繼承".myLayout"的定義,並針對該頁面的需求重新定義了"title"項目,在傳回view的名稱若為 "welcome"時,實際上會讀取的是以上的定義檔中"welcome"中的設定。

假設您的myLaout.jsp是這麼撰寫的,當中使用表格來設計版面的配置:
  • myLayout.jsp

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@taglib prefix="tiles"
uri="http://jakarta.apache.org/struts/tags-tiles"%>

<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title><tiles:getAsString name="title"/></title>
</head>
<body>
<table border="0" width="100%" cellspacing="5">
<tr>
<td colspan="2">
<tiles:insert attribute="header"/>
</td>
</tr>
<tr>
<td width="140" valign="top">
<tiles:insert attribute="menu"/>
</td>
<td valign="top" align="left">
<tiles:insert attribute="body"/>
</td>
</tr>
<tr>
<td colspan="2">
<tiles:insert attribute="footer"/>
</td>
</tr>
</table>
</body>
</html>


header、menu、body、footer等JSP頁面簡單的如下撰寫:
  • header.jsp
<%@page pageEncoding="UTF-8"%>
<center><h1>Tiles View 示範<h1> </center>

TilesViewDemo menu.jsp
<%@page pageEncoding="UTF-8"%>
<li>教學文件</li>
<li>討論區</li>
<li>我的Blog</li>
<li>推薦網站</li>

  • body.jsp
<%@page pageEncoding="UTF-8"%>
第一個Hello World的出現是在Brian Kernighan寫的"A Tutorial Introduction to
the Language B"書籍中(B語言是C語言的前身),它用來將Hello World文字顯示在電腦
螢幕上,自此之後,很多的程式語言教學文件或書籍上,已經無數次的將它當作第一個範例程式。

  • footer.jsp
<%@page pageEncoding="UTF-8"%><br>
<center>版權所有(c)http://www.caterpillar.onlyfun.net/</center>

要使用Tiles的話,您必須要有commons-beanutils.jar、commons-logging.jar、commons- collections.jar、commons-digester.jar、struts.jar四個.jar檔案,這些.jar檔案都可以在 Spring下載檔案中lib目錄的jakarta-commons目錄及struts目錄下找到。


按照以上的設定,如果您連接到/welcome.do,則會取得Tiles定義檔中的welcome的定義,結果是顯示以下的內容:


如果您有一些資料是在每一個頁面(例如在Header處)都要出現的,您可以在header.jsp中這麼撰寫:
<%@page pageEncoding="UTF-8"%>
<center><h1>Tiles View 示範<h1> </center>
<H1>${someData}</H1>

為了要能顯示${someData}資料,則您必須在每一個Controller中設定屬性值,例如:
Map model = new HashMap();
model.put("someData", data);
...

接著在ModelAndView傳回時,設定model物件,每一個Controller中都必須加入這些描述,才會在每一個有Tiles設定的頁面都能顯示${someData}。

不過還有個更簡單的方法,您可以繼承 org.springframework.web.servlet.view.tiles.ComponentControllerSupport這個類 別,它繼承自Tiles的ControllerSupport類別,您可以重新定義ComponentControllerSupport的 doPerform()方法,例如:
...
public class HeaderControllerSupport

                    extends ComponentControllerSupport {
    protected void doPerform(
                        ComponentContext componentContext,
                        HttpServletRequest request,
                        HttpServletResponse response)

                                            throws Exception {
        ApplicationContext context = getApplicationContext();
            SomeData someData =
                   (SomeData) context.getBean("someData");
        componentContext.putAttribute("someData", someData);
    }
}

在您的Tiles定義檔中可以這麼設定,就可以每一個頁面都取得someData的值了:
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE tiles-definitions PUBLIC
 "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
 "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">
 
<tiles-definitions>
    <definition name=".header"
                  path="/WEB-INF/jsp/tiles/header.jsp"
                  controllerClass="onlyfun.caterpillar.
                                  → HeaderControllerSupport"/>
    <definition name=".myLayout"
                 
path="/WEB-INF/jsp/tiles/myLayout.jsp">
      <put name="title" value="Sample Page Title"/>
      <put name="header" value=".header"/>
      <put name="menu" value="/WEB-INF/jsp/tiles/menu.jsp"/>
      <put name="footer" value="/WEB-INF/jsp/tiles/footer.jsp"/>
      <put name="body" value="/WEB-INF/jsp/tiles/body.jsp" />
    </definition>
    <definition name="welcome" extends=".myLayout">
      <put name="title" value="Tiles 測試頁面"/>
    </definition>
</tiles-definitions>

原文出處: Spring Gossip: 以 Tiles 為例
前一個主題 | 下一個主題 | 頁首 | | |



Powered by XOOPS 2.0 © 2001-2008 The XOOPS Project|