Velocity筆記(上)
雜七雜八的看了一些velocity的資料,把所見所得做個(gè)簡(jiǎn)單不系統(tǒng)的筆記寫下來(lái),算是增強(qiáng)記憶。
動(dòng)態(tài)語(yǔ)言的特性:
動(dòng)態(tài)語(yǔ)言需要一個(gè)解釋器,而這個(gè)解釋器一般在服務(wù)器中。
MVC
Model:系統(tǒng)和應(yīng)用的狀態(tài)表示,一般是類或者其他數(shù)據(jù)結(jié)構(gòu)。可以改變系統(tǒng)狀態(tài)的Actions和method。典型的javabean。設(shè)計(jì)初衷就是為了數(shù)據(jù)和顯示的分離
View:顯示結(jié)果數(shù)據(jù)的組成部分,隨著model的不同以及狀態(tài)改變,view也要相應(yīng)的做出變化。
Controller:用戶和應(yīng)用之間交互的橋梁。Controller捕獲用戶的輸入,并利用既定邏輯決定將這些命令輸入路由到哪一個(gè)model進(jìn)行處理。
只使用jsp技術(shù)叫做model 1
加入servlet后叫做model 2
什么是Velocity
Velocity is a template language designed to give Web designers an easy way to
present dynamic information to users of a Web site or application.
Velocity有個(gè)集合叫做context,說(shuō)白了就是controller和model層的封裝,提供了網(wǎng)頁(yè)模板。
實(shí)現(xiàn)了velocity的代碼將利用從context中的對(duì)象里獲取的數(shù)據(jù)替換模板中的腳本元素。
Velocity的使用簡(jiǎn)單代碼:

一個(gè)簡(jiǎn)單的語(yǔ)言描述過(guò)程就是:將velocity初始化并在context中put進(jìn)去對(duì)象,然后template加載某個(gè)vm模板,然后用template將這個(gè)vm和context進(jìn)行merge,就生成了view頁(yè)面了。
Context可以把其他類型的數(shù)據(jù)put進(jìn)來(lái),velocity會(huì)自動(dòng)的調(diào)用這些對(duì)象的toString方法。
下面重點(diǎn)說(shuō)說(shuō)context。
Context介紹
Context本質(zhì)上是一個(gè)介于java代碼層和velocity模板層之間的一個(gè)數(shù)據(jù)橋梁。Java開發(fā)人員將各種各樣的數(shù)據(jù)對(duì)象放到context中,頁(yè)面模板設(shè)計(jì)人員從context取得這些對(duì)象的reference。Velocity中定義了VelocityContext來(lái)提供基本實(shí)現(xiàn)。這個(gè)實(shí)現(xiàn)與java中的hashtable類似,最有用和常用的方法就是
Public Object put(String key, Object value);
Public Object get(String key);
如果理解了context的容器特性,那么什么東西能放進(jìn)去呢?context首先支持放一些迭代對(duì)象(Iterative Objects),比如對(duì)象數(shù)組Object[], java.util.Collection, java.util.Map, java.util.Iterator, java.util.Enumeration, 以及任意的有public Iterator iterator()方法的public class。其次context還支持put靜態(tài)類(static class),比如context.put(“Math”, Math.class)。當(dāng)然,在velocity運(yùn)行時(shí)模板 產(chǎn)生的對(duì)象也可以放到context中。
Velocity的三種reference
變量variable:對(duì)應(yīng)java對(duì)象的一種字符串化的表示,它返回的值是調(diào)用了java的toString方法后的結(jié)果。
方法method:調(diào)用所引用對(duì)象的某個(gè)方法,該方法必須是public類的一個(gè)public方法。如果該方法有返回值,那么velocity在調(diào)用完方法后會(huì)同樣的對(duì)返回值進(jìn)行toString包裝。對(duì)參數(shù)的要求是velocity要求所有的方法參數(shù)也必須是string的。
屬性property:類似方法,除了訪問java類的屬性外,還等價(jià)于get***方法。
$!前綴是quiet notation符號(hào),用這個(gè)前綴產(chǎn)生的引用在引用對(duì)象不存在的時(shí)候,會(huì)返回“”字符串而不是不存在的對(duì)象的名字的字符串。
“"”是轉(zhuǎn)義字符,可以轉(zhuǎn)義$符號(hào)和其他符號(hào)
Velocity指令
#stop:用于debug,當(dāng)engine遇到這個(gè)指令,就會(huì)停止執(zhí)行,并將控制權(quán)返回給調(diào)用程序。
#include:用于包含外部文件,將外部文件的內(nèi)容直接加入程序中。
#parse:與include類似,但是不同之處在于,include引入的是靜態(tài)的文件,而parse會(huì)動(dòng)態(tài)的加載模板,也就是說(shuō),parse會(huì)解析vm文件,然后再加入到源文件中去。
#set:就是一個(gè)很強(qiáng)大的賦值指令,不管被賦值的變量是否已經(jīng)存在或賦值,新的set指令會(huì)完全覆蓋。指令格式就是#set(ref=value)。幾個(gè)值得注意的set用法如下

List和range的用法可以等同的看做是java里的ArrayList。
對(duì)boolean值的set,set支持短路short circuit,具體代碼見下:

#end:聲明一個(gè)程序塊的結(jié)束,必須以#if或者#foreach或者#macro開始。
#if:就是普通的if,只不過(guò)在#if中,條件表達(dá)式里用true表示真,用false表示假,沒有null表達(dá)式。測(cè)試null的話直接用#if($condition)指令即可。常用的邏輯操作符(&&||!)都可以使用,比較操作符也與java相同。
#else:接著#if的使用指令,邏輯分支,同java等其他語(yǔ)言的邏輯一樣。有一點(diǎn)需要注意,沒有#else的#if需要#end,有了#else,那么#end加在#else后即可,#if后不需要再加#end。如:

#elseif:就是多分支情況下的else if合寫。對(duì)#end的要求同#else。

#foreach:直接用例子解釋:

當(dāng)然需要補(bǔ)充一點(diǎn)的是,這個(gè)循環(huán)訪問的list的生成不僅可以在頁(yè)面中定義,如上例,也可以在context中產(chǎn)生,比如在put的時(shí)候就把java的array或者實(shí)現(xiàn)了集合類的map,collection等put進(jìn)去。當(dāng)然,直接foreach一個(gè)map,那么就是訪問的map的value,想訪問它的key的話需要調(diào)用該map的keySet方法得到set。Foreach的內(nèi)置循環(huán)增量叫做$velocityCount。
#macro:就是velocity里對(duì)于c語(yǔ)言中的宏的概念,注意不是函數(shù),功能雖然類似,但是macro是在運(yùn)行時(shí)之前就確定好的,因此用#parse解析無(wú)效。語(yǔ)法就是#macro(name $param1 $param2)這樣的樣式,其中name是宏的名字,后面的param是參數(shù)。調(diào)用時(shí)候的參數(shù)的個(gè)數(shù)一定要與聲明的匹配,velocity不提供默認(rèn)參數(shù)和重載實(shí)現(xiàn)。參數(shù)類型可以是字符串,數(shù)字常量,boolean,range操作符,數(shù)組列表和velocity引用。
想定義一個(gè)全部vm通用的宏,那么要把它放在VM_global_library.vm里。
Velocimacro properties
Velocimacro.library:聲明組成velocimacro library的文件名。默認(rèn)是VM_global_library.vm
Velocimacro.permissions.allow.inline:如果設(shè)置這個(gè)屬性為false,那么內(nèi)聯(lián)的macro聲明將不被引擎執(zhí)行。默認(rèn)是true的。
Velocimacro.permissions.allow.inline.to.replace.global:聲明一個(gè)內(nèi)聯(lián)的macro是否可以override一個(gè)library提供的macro。默認(rèn)是false。
Velocimacro. permissions.allow.inline.local.scope:聲明模板是否可以提供私有的關(guān)于velocimacro的命名空間。一旦被設(shè)置為true,那么內(nèi)聯(lián)的macro只可以被本模板看到。默認(rèn)是false。
Velocimacro.context.localscope:聲明在使用velocimacro時(shí)#set指令所影響的velocity的context的行為(好蹩腳)。就是說(shuō),一旦設(shè)置這個(gè)屬性為true,velocimacro只接受本地的context。調(diào)用者注入到context中的對(duì)象將不被看到。默認(rèn)是false。
Velocimacro.library.autoreload:聲明一個(gè)修改后的velocimacro library是否在macro被調(diào)用時(shí)自動(dòng)reload。默認(rèn)是false。
Velocimacro.messages.on:聲明是否由引擎產(chǎn)生額外的log信息,默認(rèn)是true。
遞歸和嵌套macro都是允許的,但是遞歸的方法是不推薦的。
下面是講velocity是如何開始作用的:
通過(guò)調(diào)用Velocity.init()這個(gè)靜態(tài)方法,我們初始化了runtime。這個(gè)調(diào)用的結(jié)果就是運(yùn)行時(shí)的引擎通過(guò)讀取org/apache/velocity/runtime/defaults/velocity.properties文件被初始化。Velocity提供了3種定制技術(shù)去定制runtime configuration。
第一種技術(shù):自定義一個(gè)properties文件,然后通過(guò)init的帶參數(shù)方法初始化velocity引擎。
第二種技術(shù):在運(yùn)行時(shí)構(gòu)建一個(gè)java的Properties對(duì)象,然后通過(guò)set方法注入屬性,在程序代碼中傳遞給init參數(shù)。
第三種技術(shù):為了更細(xì)粒度的控制velocity引擎的初始化,一些細(xì)節(jié)的實(shí)現(xiàn)就是可以利用velocity自己的一些靜態(tài)方法在init之前去修改屬性文件。比如Velocity.setProperty(String propertyName, String value)。
更多的屬性:
Directive類的
Directive.foreach.counter.name:聲明了foreach循環(huán)中的循環(huán)變量名字,默認(rèn)是velocityCount。
Directive.foreach.counter.initial.value:聲明了循環(huán)時(shí)循環(huán)變量的初始值,默認(rèn)是1,雖然C系語(yǔ)言的初始值都為0.
Directive.include.output.errormsg.start:聲明了在include調(diào)用時(shí)輸入錯(cuò)誤參數(shù)所顯示的出錯(cuò)提示文本的開頭,默認(rèn)是“<!—include error:”。
Directive.include.output.errormsg.end:同上,代表了結(jié)尾,默認(rèn)值是“see error log -->”
Directive.parse.max.depth:這個(gè)聲明了parse命令的最大嵌套深度。默認(rèn)是10.
Encoding類的
Input.encoding:聲明被模板引擎處理的模板的編碼,默認(rèn)是ISO-8859-1.
Output.encoding:聲明關(guān)聯(lián)到輸出流的編碼,默認(rèn)是ISO-8859-1。
Logging類的
Runtime.log:聲明velocity的log文件所在的路徑,默認(rèn)是velocity.log。
Runtime.log.logsystem:聲明velocity的log任務(wù)系統(tǒng),設(shè)置的值需要實(shí)現(xiàn)org.apache.velocity.runtime.log.LogSystem接口。無(wú)默認(rèn)值。
Runtime.log.logsystem.class:聲明velocity運(yùn)行時(shí)用于處理logging服務(wù)的類名,包含一系列用逗號(hào)隔開的列表。默認(rèn)值是runtime.log.logsystem.class property is org.apache.velocity.
runtime.log.AvalonLogSystem,org.apache.velocity.runtime.log.SimpleLog4J
logSystem. Logging may be disabled by providing a value of org.apache.velocity.
runtime.log.NullLogSystem
runtime.log.error.stacktrace:
runtime.log.warn.stacktrace:
runtime.log.info.stacktrace:這三個(gè)分別用來(lái)聲明運(yùn)行時(shí)引擎在記錄錯(cuò)誤、警告和信息消息時(shí)是否開啟stacktrace。默認(rèn)值都是false。
Runtime.log.invalid.references:聲明非法引用在模板中發(fā)現(xiàn)時(shí)是否記錄的標(biāo)記,默認(rèn)是true。
Resource Management類的
Resource.manager.class:聲明處理velocity資源管理任務(wù)的類,該類必須實(shí)現(xiàn)org.apache.velocity.runtime.resource.ResourceManager接口。默認(rèn)值是org.apache.velocity.runtime.resource.ResourceManagerImpl。
Resource.manager.cache.class:聲明了資源管理緩存請(qǐng)求的類,該類必須實(shí)現(xiàn)org.apache.velocity.runtime.resource.ResourceCache接口。默認(rèn)是org.apache.velocity.runtime.resource.ResourceCacheImpl。
Resource.manager.logwhenfound:聲明了資源管理在第一次定位資源時(shí)是否記錄log。默認(rèn)是true。
Resource.loader:關(guān)聯(lián)一個(gè)名字給指定的resource loader。
<loader>.resource.loader.description:聲明了resource loader的文字描述。
<loader>.resource.loader.class:聲明了用于初始化加載關(guān)聯(lián)資源類型的類,該類需要繼承org.apache.velocity.runtime.resource.loader.ResourceLoader類,并且提供特定功能的資源類型。Velocity.properties提供了一個(gè)org.apache.velocity.runtime.resource.loader.FileResourceLoader的類。
<loader>.resource.loader.path:聲明了一個(gè)根目錄用來(lái)存放相關(guān)類型的資源。Velocity.properties提供了.作為根目錄。
<loader>.resource.loader.cache:聲明loader是否要緩存特定資源。一般建議true。
<loader>.resource.loader.modificationCheckInterval:聲明了檢查緩存資源是否有修改的時(shí)間間隔,以秒為單位。默認(rèn)是2.
其他類的
Runtime.initerpolate.string.literals:聲明了模板引擎是否要插入字符串常量,默認(rèn)是true。
Parser.pool.size:聲明了啟動(dòng)時(shí)runtime創(chuàng)建的parser pool的大小,默認(rèn)是20.
Resource loaders資源加載
一個(gè)velocity的resource簡(jiǎn)單說(shuō)就是一個(gè)提供給模板引擎的輸入。包括正規(guī)模板、velocimacro的library、由include指令引入的普通文本。一個(gè)resource loader就是一個(gè)知道如何從這些特定的resource中提取資源的實(shí)體。一般用到的就是file resource loader,當(dāng)然velocity還提供了3種其他loader類型:JAR, Classpath和DataSource。
Events事件
為了提供模板處理時(shí)的更好控制,velocity提供了事件處理等級(jí),支持用戶干預(yù)。
有三種事件類型:
第一, 在#set指令中為引用賦NULL的時(shí)候。
第二, Java方法調(diào)用velocity方法或?qū)傩砸贸霈F(xiàn)異常的時(shí)候。
第三, 每次velocity引用對(duì)應(yīng)的值插入輸出流的時(shí)候。
Velocity提供了NullSetEventHandler, MethodExceptionEventHandler和ReferenceInsertionEventHandler接口去處理這些事件。
Context chaining
簡(jiǎn)單講就是一層層的包裝context(直覺讓我想起façade模式),如果有重復(fù)的話,最外層的覆蓋最里層的,但是本層的context對(duì)于重復(fù)的key還是可以保持可訪問。實(shí)現(xiàn)方式是通過(guò)VelocityContext的重載構(gòu)造方法。
Velocity和XML
velocity支持對(duì)xml的處理,只要加入類似這樣的代碼即可

接著在displayxml.vm這樣寫:

然后就可以咯。
如何輸出XML與輸出HTML是一致的。
結(jié)合velocity和servlet
其實(shí)很簡(jiǎn)單,寫一個(gè)類,繼承VelocityServlet。不需要普通的doGet和doPost方法,取而代之的是一個(gè)handleRequest方法,該方法傳遞3個(gè)參數(shù),HttpServletRequest, HttpServletResponse和Context。前兩個(gè)參數(shù)與普通的servlet一樣,最后一個(gè)參數(shù)就對(duì)應(yīng)了velocity模板引擎的context。該方法返回一個(gè)Template。基礎(chǔ)代碼如下:

HttpServletRequest和HttpServletResponse在context中同樣存在,名字分別是req和res,我們可以寫這樣的代碼去訪問和讀取request和response:
#set($username = $req.getParameter(‘username’))
VelocityServlet非常強(qiáng)大,除了簡(jiǎn)單的handleRequest方法外,還包括了一系列方法:
Properties loadConfiguration(ServletConfig):允許添加額外的servlet屬性。
Context createContext(HttpServletRequest, HttpServletResponse):允許開發(fā)者創(chuàng)建自己的context,這個(gè)context可以進(jìn)行似有的merge。
void setContentType( HttpServletRequest,HttpServletResponse):設(shè)置內(nèi)容類型,默認(rèn)的是text/html格式。
void mergeTemplate(Template, Context, HttpServletResponse):自行控制合并模板的方法,可以繞過(guò)handleRequest
void requestCleanup(HttpServletRequest, HttpServletResponse,Context):自行合并后的清理操作。
protected void error(HttpServletRequest, HttpServletResponse, Exception):錯(cuò)誤處理。
Velocity和turbine的結(jié)合
可以說(shuō)turbine是velocity的最好舞臺(tái),它天然的結(jié)合了velocity。
Turbine的三個(gè)特性:
1. 基于servlet作為控制器
2. 強(qiáng)調(diào)安全繼承
3. 獨(dú)立于web使用。
Turbine的MVC經(jīng)典組合就是EJB作為模型,servlet寫控制器,而velocity是顯示。
先看看turbine的架構(gòu):

5大模塊,都在assembler下面。其中
Action:完成特定任務(wù)的代碼。
Navigation:用來(lái)顯示導(dǎo)航鏈接和控制的velocity模板。
Screen:velocity模板和java類的組合,用來(lái)顯示layout模塊內(nèi)的核心內(nèi)容。
Layout:velocity模板,用來(lái)描述頁(yè)面如何工作。
Page:一個(gè)包含了所有這些模塊的概念級(jí)別的對(duì)象。
先來(lái)看看action模塊:

這個(gè)圖是action和其他模塊之間的一個(gè)流動(dòng)視圖。當(dāng)有一個(gè)get或者post請(qǐng)求時(shí),page模塊將執(zhí)行action模塊,由action模塊判斷由哪個(gè)screen來(lái)顯示信息。
Navigation模塊:
說(shuō)白了就是webx中的control,控制頁(yè)面的上下左右各個(gè)部分,表達(dá)不同的所謂navigation schemes。這個(gè)模塊是被layout執(zhí)行的。
Screen模塊:
Navigation模塊展示了頁(yè)面的各個(gè)邊緣組成,那么screen就負(fù)責(zé)顯示頁(yè)面的核心內(nèi)容。
Layout模塊:
Layout如其名,就是通過(guò)布局來(lái)控制哪里安排navigation,哪里顯示screen。
Page模塊:
Page是整個(gè)web應(yīng)用的外殼,請(qǐng)求也是先到達(dá)page的。Page更像一個(gè)容器,用來(lái)管理和聯(lián)系整個(gè)的各個(gè)模塊。

這個(gè)圖顯示了各個(gè)模塊的包裝關(guān)系。
Turbine定義了5個(gè)loader去加載這5個(gè)模塊,loader的結(jié)構(gòu)圖如下:

未完待續(xù)~~
參考資料:
http://velocity.apache.org/里的user guide和developer guide
《Wiley - Mastering Apache Velocity》,Joseph D. Gradecki, Jim Cole