(從csdn的blog上同步過(guò)來(lái))
(本文發(fā)于java emag第一期)

關(guān)于 Template JSP 的起源還要追述到 Web 開發(fā)的遠(yuǎn)古年代,那個(gè)時(shí)候的人們用 CGI 來(lái)開發(fā) web 應(yīng)用,在一個(gè) CGI 程序中寫 HTML 標(biāo)簽。

在這之后世界開始朝不同的方向發(fā)展: sun 公司提供了類似于 CGI servlet 解決方案,但是無(wú)論是 CGI 還是 servlet 都面對(duì)同一個(gè)問(wèn)題:在程序里寫 html 標(biāo)簽,無(wú)論如何都不是一個(gè)明智的解決方案。于 sun 公司 1999年推出了JSP技術(shù)。 而在另一個(gè)世界里,以 PHP ASP 為代表的 scriptlet 頁(yè)面腳本技術(shù)開始廣泛應(yīng)用。

不過(guò)即便如此,問(wèn)題并沒(méi)有結(jié)束,新的問(wèn)題出現(xiàn)了:業(yè)務(wù)和 HTML 標(biāo)簽的混合,這個(gè)問(wèn)題不僅導(dǎo)致頁(yè)面結(jié)構(gòu)的混亂,同時(shí)也使代碼本身難以維護(hù)。

于是來(lái)自起源于 70 年代后期的 MVC 模式被引入開發(fā)。 MVC 的三個(gè)角色: Model ——包含除 UI 的數(shù)據(jù)和行為的所有數(shù)據(jù)和行為。 View 是表示 UI 中模型的顯示。任何信息的變化都由 MVC 中的第三個(gè)成員來(lái)處理——控制器。

在之后的應(yīng)用中,出現(xiàn)了技術(shù)的第一次飛躍:前端的顯示邏輯和后端的業(yè)務(wù)邏輯分離, COM 組件或 EJB CORBA 用于處理業(yè)務(wù)邏輯, ASP 、 JSP 以及 PHP 被用于前端的顯示。這個(gè)就是 Web 開發(fā)的 Model 1 階段(頁(yè)面控制器模式)。

不過(guò)這個(gè)開發(fā)模式有很多問(wèn)題:

1.?????? 頁(yè)面中必須寫入 Scriptlet 調(diào)用組件以獲得所必需的數(shù)據(jù)。

2.?????? 處理顯示邏輯上 Scriptlet 代碼和 HTML 代碼混合交錯(cuò)。

3.?????? 調(diào)試?yán)щy。 JSP 被編譯成 servlet ,頁(yè)面上的調(diào)試信息不足以定位錯(cuò)誤。

這一切都是因?yàn)樵?/span> Model 1 中并沒(méi)有分離視圖和控制器。完全分離視圖和控制器就成了必須。這就是 Model 2 。它把 Model 1 中未解決的問(wèn)題——分離對(duì)組件(業(yè)務(wù)邏輯)的調(diào)用工作,把這部分工作移植到了控制器?,F(xiàn)在似乎完美了,不過(guò)等等,原來(lái)的控制器從頁(yè)面中分離后,頁(yè)面所需的數(shù)據(jù)怎么獲得,誰(shuí)來(lái)處理頁(yè)面顯示邏輯??jī)蓚€(gè)辦法: 1. 繼續(xù)利用 asp , php 或者 jsp 等機(jī)制,不過(guò)由于它們是運(yùn)行在 web 環(huán)境下的,他們所要顯示的數(shù)據(jù)(后端邏輯產(chǎn)生的結(jié)果)就需要通過(guò)控制器放入 request 流中; 2. 使用新手法——模板技術(shù),使用獨(dú)立的模板技術(shù)由于脫離的 web 環(huán)境,會(huì)給開發(fā)測(cè)試帶來(lái)相當(dāng)?shù)谋憷V劣陧?yè)面所需數(shù)據(jù)傳入一個(gè) POJO 就行而不是 request 對(duì)象。

模板技術(shù)最先開始于 PHP 的世界,出現(xiàn)了 PHPLIB Template FastTemplate這兩位英雄。不久模板技術(shù)就被引入到java web開發(fā)世界里。目前比較流行的模板技術(shù)有:XSTLVelocity, JDynamiTe ,Tapestry等。另外因?yàn)?span lang="EN-US">JSP技術(shù)畢竟是目前標(biāo)準(zhǔn),相當(dāng)?shù)南到y(tǒng)還是利用JSP來(lái)完成頁(yè)面顯示邏輯部分,在Sun公司的JSTL外,各個(gè)第三方組織也紛紛推出了自己的Taglib,一個(gè)代表是struts tablib。

模板技術(shù)從本質(zhì)上來(lái)講,它是一個(gè)占位符動(dòng)態(tài)替換技術(shù)。一個(gè)完整的模板技術(shù)需要四個(gè)元素: 0. 模板語(yǔ)言, 1. 包含模板語(yǔ)言的模板文件, 2. 擁有動(dòng)態(tài)數(shù)據(jù)的數(shù)據(jù)對(duì)象, 3. 模板引擎。以下就具體討論這四個(gè)元素。(在討論過(guò)程中,我只列舉了幾個(gè)不同特點(diǎn)技術(shù),其它技術(shù)或有雷同就不重復(fù)了)

模板語(yǔ)言包括:變量標(biāo)識(shí)和表達(dá)式語(yǔ)句。根據(jù)表達(dá)式的控制力不同,可以分為強(qiáng)控制力模板語(yǔ)言和弱控制力模板語(yǔ)言。而根據(jù)模板語(yǔ)言與 HTML 的兼容性不同,又可以分為兼容性模板語(yǔ)言和非兼容性模板語(yǔ)言。

模板語(yǔ)言要處理三個(gè)要點(diǎn):

1. 標(biāo)量標(biāo)記。把變量標(biāo)識(shí)插入 html 的方法很多。其中一種是使用類似 html 的標(biāo)簽;另一種是使用特殊標(biāo)識(shí),如 Velocity 或者 JDynamiTe ;第三種是擴(kuò)展 html 標(biāo)簽,如 tapestry 。采用何種方式有著很多考慮,一個(gè)比較常見的考慮是“所見即所得”的要求。

2. 條件控制。這是一個(gè)很棘手的問(wèn)題。一個(gè)簡(jiǎn)單的例子是某物流陪送系統(tǒng)中,物品數(shù)低于一定值的要高亮顯示。不過(guò)對(duì)于一個(gè)具體復(fù)雜顯示邏輯的情況,條件控制似乎不可避免。當(dāng)你把類似于 <IF condition=”$count < 40”><then><span class=”highlight”>count </span></then></IF> 引入,就象我們當(dāng)初在 ASP PHP 中所做得一樣,我們將不得不再一次面對(duì) scriptlet 嵌入網(wǎng)頁(yè)所遇到的問(wèn)題。我相信你和我一樣并不認(rèn)為這是一個(gè)好得編寫方式。實(shí)際上并非所有的模板技術(shù)都使用條件控制,很多已有的應(yīng)用如 PHP 的以及我曾見過(guò)一個(gè)基于 ASP.NET 的應(yīng)用,當(dāng)然還有 Java JDynamiTe 。這樣網(wǎng)頁(yè)上沒(méi)有任何邏輯,不過(guò)這樣做的代價(jià)是把高亮顯示的選擇控制移交給編程代碼。你必需做個(gè)選擇。也許你也象我一樣既不想在網(wǎng)頁(yè)中使用條件控制,也不想在代碼中寫 html 標(biāo)記,但是這個(gè)顯示邏輯是無(wú)可逃避的(如果你不想被你的老板抄魷魚的話),一個(gè)可行的方法是用 CSS ,在編程代碼中決定采用哪個(gè) css 樣式。特別是 CSS2 技術(shù),其 selector 機(jī)制,可以根據(jù) html 類型甚至是 element attributes 來(lái) apply 不同的樣式。

3. 迭代(循環(huán))。在網(wǎng)頁(yè)上顯示一個(gè)數(shù)據(jù)表單是一個(gè)很基本的要求,使用集合標(biāo)簽將不可避免,不過(guò)幸運(yùn)的是,它通常很簡(jiǎn)單,而且夠用。特別值得一提的是 PHP 的模板技術(shù)和 JDynamiTe 技術(shù)利用 html 的注釋標(biāo)簽很簡(jiǎn)單的實(shí)現(xiàn)了它,又保持了“所見既所得”的特性。

下面是一些技術(shù)的比較:

Velocity

變量定義:用 $ 標(biāo)志

表達(dá)式語(yǔ)句:以 # 開始

強(qiáng)控制語(yǔ)言:變量賦值: #set $this = "Velocity"

??????????? 外部引用: #include ( $1 )

??????????? 條件控制: #if …. #end

非兼容語(yǔ)言

JDynamiTe

變量定義:用 {} 包裝

表達(dá)式語(yǔ)句:寫在注釋格式( <!--? à )中

弱控制語(yǔ)言

兼容語(yǔ)言

XSLT

變量定義: xml 標(biāo)簽

表達(dá)式: xsl 標(biāo)簽

強(qiáng)控制語(yǔ)言:外部引用: import , include

??????????? 條件控制: if , ? choose…when…otherwise

非兼容語(yǔ)言

Tapestry

采用 component 的形式開發(fā)。

變量定義(組件定義):在 html 標(biāo)簽中加上 jwcid

表達(dá)式語(yǔ)句: ognl 規(guī)范

兼容語(yǔ)言

?

模板文件指包含了模板語(yǔ)言的文本文件。

模板文件由于其模板語(yǔ)言的兼容性導(dǎo)致不同結(jié)果。與 HTML 兼容性的模板文件只是一個(gè)資源文件,其具有良好的復(fù)用性和維護(hù)性。例如 JDynamiTe 的模板文件不但可以在不同的項(xiàng)目中復(fù)用,甚至可以和 PHP 程序的模板文件互用。而如 velocity 的非兼容模板文件,由于其事實(shí)上是一個(gè)腳本程序,復(fù)用性和可維護(hù)性大大降低。

模板文件包含的是靜態(tài)內(nèi)容,那么其所需的動(dòng)態(tài)數(shù)據(jù)就需要另外提供。根據(jù)提供數(shù)據(jù)方式的不同可以分為 3 種:

1.?????? Map :利用 key/value 來(lái)定位。這個(gè)是最常見的技術(shù)。如 velocity VelocityContext 就是包含了 map 對(duì)象。

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 數(shù)據(jù)對(duì)象,如 XSLT 利用 XPath 技術(shù)。

3.?????? POJO :直接利用反射取得 DTO 對(duì)象,利用 JavaBean 機(jī)制取得數(shù)據(jù)。如 Tapestry 。

模板引擎的工作分為三步:

1. 取得模板文件并確認(rèn)其中的模板語(yǔ)言符合規(guī)范。

比如 velocity ,確定 #if 有對(duì)應(yīng)得 #end 等。 Xml xslt 的模型中, xml 文件標(biāo)簽是否完整等。在完成這些工作后,模板引擎通常會(huì)把模板文件解析成一顆節(jié)點(diǎn)樹(包含模板文件的靜態(tài)內(nèi)容節(jié)點(diǎn)和模板引擎所定義的特殊節(jié)點(diǎn))。

2. 取得數(shù)據(jù)對(duì)象。

?????? ? 該數(shù)據(jù)對(duì)象一般通過(guò)程序傳遞引用實(shí)現(xiàn)?,F(xiàn)有的大量框架在程序底層完成,處理方式也各自不同,有兩種技術(shù)分別為推技術(shù)和拉技術(shù)。推技術(shù): controller 調(diào)用 set 方法把動(dòng)態(tài)數(shù)據(jù)注入,模板引擎通過(guò) get 方法獲得,典型代表: Struts ;拉技術(shù):模板引擎根據(jù)配置信息,找到與 view 對(duì)應(yīng)的 model ,調(diào)用 model get 方法取得數(shù)據(jù),典型代表: Tapestry

3. 合并模板文件(靜態(tài)內(nèi)容)和數(shù)據(jù)對(duì)象(動(dòng)態(tài)內(nèi)容),并生成最終頁(yè)面。

?????? ? 合并的機(jī)制一般如下,模板引擎遍歷這顆節(jié)點(diǎn)樹的每一個(gè)節(jié)點(diǎn),并 render 該節(jié)點(diǎn),遇到靜態(tài)內(nèi)容節(jié)點(diǎn)按正常輸入,遇到特殊節(jié)點(diǎn)就從數(shù)據(jù)對(duì)象中去得對(duì)應(yīng)值,并執(zhí)行其表達(dá)式語(yǔ)句(如果有的話)。

以下詳細(xì)說(shuō)明:

Velocity

Template template = Velocity.getTemplate("test.wm");

Context context = new VelocityContext();

context.put("foo", "bar");

context.put("customer", new Customer());

template.merge(context, writer);

當(dāng)調(diào)用 Velocity.getTemplate 方法時(shí),將調(diào)用 ResourceManger 的對(duì)應(yīng)方法。

ResourceManger 先查看該模板文件是否在 cache 中,如果沒(méi)有就去獲取,生成 resource 對(duì)象并調(diào)用 process() 方法,確定該模板是否有效,如果有效,則在內(nèi)存中生成一個(gè) Node 樹。

當(dāng)調(diào)用 template.merge() 時(shí),遍歷這顆 Node 樹,并調(diào)用每個(gè) Node render 方法。對(duì)于模板中的變量和對(duì)象 Node ,還將調(diào)用 execute() 方法,從 context 中取得 value

?? 注: ResourceManger runtime\resource 包下, Node runtime\parser\node 包下

Tapestry

Tapestry 比較麻煩,先介紹一下 http 請(qǐng)求的處理過(guò)程。

當(dāng) httprequest 請(qǐng)求到達(dá)時(shí)。該請(qǐng)求被 ApplicationServlet 捕獲,隨后 ApplicationServlet 通過(guò) getEngine 取到對(duì)應(yīng)的 Engine ,通過(guò)該 engine getService 拿到對(duì)應(yīng)的 service ,調(diào)用其 service 方法執(zhí)行 http 請(qǐng)求。

每個(gè) service 通過(guò) RequestCycle 對(duì)象的 getPage 方法取得 Page 對(duì)象,并將其設(shè)置為該 Cycle 對(duì)象的 active Page 。之后 service 調(diào)用 renderResponse 方法執(zhí)行輸出。

renderResponse 調(diào)用 page getResponseWriter(output) 取得 writer 對(duì)象,并把它傳給 cycle.renderPage(writer) 方法,該方法調(diào)用 page renderPage 方法。

Page 執(zhí)行 renderPage 時(shí),首先判斷是否有 listener 的請(qǐng)求,如果有則處理 listener 請(qǐng)求;然后調(diào)用 BaseComponentTemplateLoader process 方法把模板文件載入并形成一個(gè) component 節(jié)點(diǎn)樹,依次執(zhí)行節(jié)點(diǎn)的 renderComponent 方法。

每個(gè) component 對(duì)象將通過(guò) ongl 的機(jī)制取得對(duì)象屬性。并把該值寫入輸入流。

例如: 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 屬性。

?

技術(shù)分析

?????? 技術(shù):

?????? JSP ,一個(gè)偽裝后的 servlet web server 會(huì)對(duì)任何一個(gè) jsp 都生成一個(gè)對(duì)應(yīng) jsp 類,打開這個(gè)類,就會(huì)發(fā)現(xiàn), jsp 提供的是一個(gè)代碼生成機(jī)制,把 jsp 文件中所有的 scriptlet 原封不動(dòng)的 copy 生成的 jsp 類中,同時(shí)調(diào)用 println 把所有的 html 標(biāo)簽輸出。

Test.jsp

<html>

<head><title>jsp test</title></head>

<body>

<table width="226" border="0" cellspacing="0" cellpadding="0">

?? <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>\r\n");

????? out.write("<head><title>jsp test</title></head> \r\n");

????? out.write("<body>\r\n");

????? out.write("<table width=\"226\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\r\n?? ");

????? out.write("<tr><td><font face=\"Arial \" size=\"2\" color=\"#000066\"> \r\n\t????? ");

????? out.write("<b class=\"headlinebold\">The jsp test file");

????? out.write("</b>\r\n\t ?????");

????? out.write("</tr></td></font>\r\n\t ");

????? out.write("</table>\r\n");

????? out.write("<body>\r\n");

????? 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);

??? }

? }

}

?

?????? 技術(shù):

Taglib 作為 jsp 之上的輔助技術(shù),其工作本質(zhì)依托與 jsp 技術(shù),也是自定義標(biāo)簽翻譯成 java 代碼,不過(guò)這次和 jsp 略有不同,它還要經(jīng)過(guò)幾個(gè)過(guò)程。

先來(lái)看一下,實(shí)現(xiàn)一個(gè) tag 2 個(gè)要點(diǎn):

1. 提供屬性的set方法,此后這個(gè)屬性就可以在jsp頁(yè)面設(shè)置。以jstl標(biāo)簽為例 c:out value=""/,這個(gè)value就是jsp數(shù)據(jù)到tag之間的入口。所以tag里面必須有一個(gè)setValue方法,具體的屬性可以不叫value。例如setValue(String data){this.data = data;}。這個(gè)“value”的名稱是在tld里定義的。取什么名字都可以,只需tag里提供相應(yīng)的set方法即可。

2. 處理 doStartTag doEndTag 。這兩個(gè)方法是 TagSupport提供的。還是以c:out value=""/為例,當(dāng)jsp解析這個(gè)標(biāo)簽的時(shí)候,在“<”處觸發(fā) doStartTag 事件,在“>”時(shí)觸發(fā) doEndTag 事件。通常在 doStartTag 里進(jìn)行邏輯操作,在 doEndTag 里控制輸出。

? 在處理tag的時(shí)候:

? 0. tagPool中取得對(duì)應(yīng)tag。

1.???? 為該tag設(shè)置頁(yè)面上下文。

2.???? 為該tag設(shè)置其父tag,如果沒(méi)有就為null

3.???? 調(diào)用setter方法傳入標(biāo)簽屬性值tag,如果該標(biāo)簽沒(méi)有屬性,此步跳過(guò)。

4.???? 調(diào)用doStartTag方法,取的返回值。

5.???? 如果該標(biāo)簽有body,根據(jù)doStartTag返回值確定是否pop該標(biāo)簽內(nèi)容。如果要popbody,則:setBodyContent(),在之后,doInitBody()。如果該標(biāo)簽沒(méi)有body,此步跳過(guò)。

6.???? 調(diào)用doEndTag()以確定是否跳過(guò)頁(yè)面剩下部分。

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 頁(yè)面:

<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 技術(shù)提供兩個(gè)機(jī)制,Bodynon-Body導(dǎo)致了taglib的出現(xiàn)了兩個(gè)分支:Display TagControl Tag, 前者在java code中嵌入了html標(biāo)簽,相當(dāng)與一個(gè)web component,而后者則是另一種模板腳本。

?

?????? 1. 技術(shù)學(xué)習(xí)難易度

模板技術(shù)。使用模板技術(shù),第一點(diǎn)就是必須學(xué)習(xí)模板語(yǔ)言,尤其是強(qiáng)控制的模板語(yǔ)言。于是模板語(yǔ)言本身的友好性變的尤為重要。以下依據(jù)友好性,表現(xiàn)力以及復(fù)用性三點(diǎn)為主基點(diǎn)比較了一下幾種模板技術(shù)。

Velocity

Turbine 項(xiàng)目( http://jakarta.apache.org/Turbine )采用了 velocity 技術(shù)。

1.?????? 友好性不夠。理由: 強(qiáng)控制類型,出現(xiàn)頁(yè)面顯示控制代碼和 html 混合。與 Html 的不兼容,無(wú)法所見即所得。遇到大的 HTML 頁(yè)面,從一個(gè) #if ”找到對(duì)應(yīng)的 #end ”也是很痛苦的一件事情。

2.?????? 表現(xiàn)力強(qiáng)。理由:強(qiáng)控制語(yǔ)言。

3.?????? 復(fù)用性弱。理由:模板腳本和頁(yè)面代碼混合。

XSLT

Cocoon 項(xiàng)目( http://cocoon.apache.org/ )采用 XML + XSLT 的方法。 CSDN 社區(qū)也是采用此方案。

1.?????? 內(nèi)容和顯示風(fēng)格分離,這點(diǎn) XSLT 做的最好。

2.?????? 速度慢。理由: XSLT 的使用 XPath ,由于是要解析 DOM 樹,當(dāng) XML 文件大時(shí),速度很慢。

3.?????? 友好性不夠。理由:由于沒(méi)有 HTML 文件,根本看不到頁(yè)面結(jié)構(gòu)、顯示風(fēng)格和內(nèi)容。 XSL 語(yǔ)法比較難以掌握,由于沒(méi)有“所見即所得”編輯工具,學(xué)習(xí)成本高。

4.?????? 表現(xiàn)力強(qiáng)。理由:強(qiáng)控制語(yǔ)言。

5.?????? 復(fù)用性弱。理由: xsl 標(biāo)簽和 html 標(biāo)簽混合。

JDynamiTe

1.?????? 表現(xiàn)力中等。理由:弱控制語(yǔ)言。

2.?????? 友好性強(qiáng)。理由:所見即所得的效果。在模板件中的 ignore block 在編輯條件下可展示頁(yè)面效果,而在運(yùn)行中不會(huì)被輸出。

3.?????? 復(fù)用性強(qiáng)。理由:利用 html 標(biāo)簽。

Tapestry

1.?????? 友好性中等。理由:整個(gè) Tapestry 頁(yè)面文件都是 HTML 元素。但是由于 component 會(huì)重寫 html 標(biāo)簽,其顯示的樣子是否正確,將不預(yù)測(cè)。

2.?????? 表現(xiàn)力強(qiáng)。理由:強(qiáng)控制語(yǔ)言。

3.?????? 復(fù)用性強(qiáng)。理由:擴(kuò)展了 HTML 元素的定義。

?

?

JSP 中大量的使用 TagLib ,能夠使得 JSP 的頁(yè)面結(jié)構(gòu)良好,更符合 XML 格式,而且能夠重用一些頁(yè)面元素。但 TagLib 的編譯之后的代碼龐大而雜亂。 TabLib 很不靈活,能完成的事情很有限。 TabLib 代碼本身的可重用性受到 TagSupport 定義的限制,不是很好。 另外是,我不得不承認(rèn)的一件事是, TagLib 的編寫本身不是一件愉快的事情,事實(shí)我個(gè)人很反對(duì)這種開發(fā)方式。

?

?????? 2. 技術(shù)使用難易度

?????? 模板技術(shù):模板技術(shù)本身脫離了 Web 環(huán)境,可以在不啟動(dòng) Web server 得情況下進(jìn)行開發(fā)和測(cè)試,一旦出錯(cuò)詳細(xì)的信息易于錯(cuò)誤的定位。由于模板引擎的控制,頁(yè)面中將只處理顯示邏輯(盡管其可能很復(fù)雜)

?????? JSP 技術(shù):工作在 Web 環(huán)境下,開發(fā)測(cè)試一定要運(yùn)行 web server 。此外,一些 TagLib 能夠產(chǎn)生新的標(biāo)簽,頁(yè)面的最終布局也必須在 web 環(huán)境下才可以確定。測(cè)試時(shí)出錯(cuò)信息不明確,特別是 TagLib 得存在,極不容易定位。由于其本質(zhì)是程序,很容易在其中寫入業(yè)務(wù)邏輯,甚至于數(shù)據(jù)庫(kù)連接代碼,造成解耦的不徹底。

?

3. 總結(jié)

模板技術(shù)更加專注于頁(yè)面的顯示邏輯,有效幫助開發(fā)人員分離視圖和控制器。在學(xué)習(xí),開發(fā)和測(cè)試都更加容易。

JSP 技術(shù)本身是一個(gè)早期的技術(shù),本身并沒(méi)有提出足夠的方式來(lái)分離視圖和控制器。相反,我認(rèn)為其本身是鼓勵(lì)開發(fā)人員不做解耦,因?yàn)樵?/span> JSP 代碼中插入業(yè)務(wù)邏輯是如此的容易。