Velocity筆記(上)
雜七雜八的看了一些velocity的資料,把所見所得做個簡單不系統的筆記寫下來,算是增強記憶。
動態語言的特性:
動態語言需要一個解釋器,而這個解釋器一般在服務器中。
MVC
Model:系統和應用的狀態表示,一般是類或者其他數據結構。可以改變系統狀態的Actions和method。典型的javabean。設計初衷就是為了數據和顯示的分離
View:顯示結果數據的組成部分,隨著model的不同以及狀態改變,view也要相應的做出變化。
Controller:用戶和應用之間交互的橋梁。Controller捕獲用戶的輸入,并利用既定邏輯決定將這些命令輸入路由到哪一個model進行處理。
只使用jsp技術叫做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有個集合叫做context,說白了就是controller和model層的封裝,提供了網頁模板。
實現了velocity的代碼將利用從context中的對象里獲取的數據替換模板中的腳本元素。
Velocity的使用簡單代碼:

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

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

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

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

#foreach:直接用例子解釋:

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

接著在displayxml.vm這樣寫:

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

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

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

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

這個圖顯示了各個模塊的包裝關系。
Turbine定義了5個loader去加載這5個模塊,loader的結構圖如下:

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