《Velocity java開發(fā)指南》中文版
聲明: 轉(zhuǎn)載請保留此頁聲明
**************************************************************************
如對您有所幫助,我們不勝榮幸!
*************************************************************************
本文屬NetJava.cn中的Velocity中文系列,本系包含如下文章:
《Velocity Java開發(fā)指南中文版》(Developer`s Guide)
《Velocity模板使用指南中文版》(User`s Guide)
《Velocity Web應(yīng)用開發(fā)指南中文版》(Web Application Guide)
《VTL語法參考指南中文版》(VTL Reference)
《DB4O中文系列之起步篇》
. . .
**************************************************************************
譯者: javaFound
*************************************************************************
目 錄
Velocity是一基于java語言的模板引擎,使用這個簡單、功能強(qiáng)大的開發(fā)工具,可以很容易的將數(shù)據(jù)對象靈活的與格式化文檔組裝到一起;希望本文能指引使用velocity在開發(fā)基于servlet或一般java應(yīng)用程序的應(yīng)用上快速起步。
取得Velocity并在你的機(jī)器上開始運(yùn)行很容易,以下是全部詳細(xì)的說明:
- 取得Velocity發(fā)布版本,go here。
- 目錄及文件說明:
- Velocity-X.jar 完整的velocity jar包一般命名格式為velocity-X.jar,其中X是當(dāng)前版本號。注意這個jar包不包含Velocity所必須依賴的其它jar包(具體見后)。
- SRC:完整的源文件代碼目錄
- Examples. 完整的aplication或web App例子。
- docs :Veocity文檔目錄
- build: 使用ant編譯源碼時所需的lib.
- OK,現(xiàn)在就可以開始使用了.請將Velocity-x.jar放到你的classpath中或webapp的lib下。
當(dāng)然,我們強(qiáng)烈建議你先運(yùn)行其中的例子,以感受Velocity的優(yōu)異之處.
Velocity可運(yùn)行于JDK1.4或JRE1.4及其以上版本.
Velocity也依賴于其它一些jar包,在分發(fā)版本的 build/lib 有,如果你下載的是二進(jìn)制分發(fā)版本,需要到以下地址下載其它依賴包.
一些優(yōu)秀的資源和例程列表如下:
- 開發(fā)者郵件列表 mail-lists.
- 郵件檔案表 : http://www.是很好的一個資源庫.可以以’Velocity’為關(guān)鍵字進(jìn)行搜索。
- 源代碼(源碼分發(fā)版本) : src/java/... : 含有Velocity project的所有源碼
- 應(yīng)用程序例程1 : examples/app_example1 : 一個很簡單的示例如何在一般應(yīng)用程序中使用Velocity.
- 應(yīng)用程序例程1 2 : examples/app_example2 : 如何在應(yīng)用程序中使用Velocity工具類.
- servlet example : examples/servlet_example1 :示例如何在servlet中用Velocity 輸出模板.
- logger example : examples/logger_example : 如何定制Velocity的日志工具.
- XML example : examples/xmlapp_example : 使用 JDOM 從 Velocity 模板讀取內(nèi)容. 還包含一個遞歸調(diào)用宏的示例.
- event example : examples/event_example : 在Velocity 1.1 中使用事件處理API。
- Anakia application : examples/anakia : 示例用stylesheet 美化 xml 數(shù)據(jù)。
- Forumdemo web app : examples/forumdemo : 一個基于servlet的論壇功能實(shí)現(xiàn)示例.
- templates : test/templates :全面展示VTL(Velocity Template Lanauage)功能的模板集合。
context example : examples/context_example : 兩個示例如何重寫(繼承) Velocity context 功能的例子(針對高級用戶).
在application program或servlet中使用Velocity中,一般通過如下步驟:
- 對于所有應(yīng)用,第一步是要初始化Velocity, 一般使用唯一實(shí)例模式(Singleton),如Velocity.init().
- 創(chuàng)建一個Context object.
- 將你的數(shù)據(jù)對象加入到Context對象中.
- 使用Velocity選擇一個模板.
- 合并模板和數(shù)據(jù)導(dǎo)出到輸出流.
下面的代碼,通過使用org.apache.velocity.app.Velocity的單實(shí)例模式,合并輸出:
import java.io.StringWriter;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.MethodInvocationException;
//初始化
Velocity.init();
//取得VelocityContext對象
VelocityContext context = new VelocityContext();
//向context中放入要在模板中用到的數(shù)據(jù)對象
context.put( "name", new String("Velocity") );
Template template = null;
//選擇要用到的模板
try
{
template = Velocity.getTemplate("mytemplate.vm");
}
catch( ResourceNotFoundException rnfe )
{
// couldn‘t find the template
}
catch( ParseErrorException pee )
{
// syntax error : problem parsing the template
}
catch( MethodInvocationException mie )
{
// something invoked in the template
// threw an exception
}
catch( Exception e )
{}
StringWriter sw = new StringWriter();
//合并輸出
template.merge( context, sw );
以上是基本的使用模式,看起來非常簡潔!這些都是一般情況下使用Velocity所必須的步驟. 但你可能不想這樣按部就班的編寫代碼 –Velocity提供了一些工具以更容易的方式在servlet或應(yīng)用程序中使用。在這個指南的后面, 我們將討論在servlet和普通應(yīng)用程序中更好的用法.
這是系統(tǒng)默認(rèn)的模式, 這樣在jvm(應(yīng)用程序)或web aplication(一個web程序)中只存在一個Velocity engine實(shí)例共享使用。. 這對于配置和共享資源來說非常方便. 比如, 這非常適合用于支持 Servlet 2.2+ 的web application中,每一個web app持有它自己的唯一Velocity實(shí)例, 它們可以共享templates, a logger等資源. Singleton可以直接通過使用org.apache.velocity.app.Velocity 類, 如下例子:
import org.apache.velocity.app.Velocity;
import org.apache.velocity.Template;
/*
* Configure the engine - as an example, we are using
* ourselves as the logger - see logging examples
*/
Velocity.setProperty( Velocity.RUNTIME_LOG_LOGSYSTEM, this);
/*
* now initialize the engine
*/
Velocity.init();
Template t = Velocity.getTemplate("foo.vm");
在1.2版本以后, 可以在 JVM (or web application.)創(chuàng)建,配置,使用多個Velocity實(shí)例;當(dāng)你希望在同一程序中,對每個實(shí)例獨(dú)立配置時它們的 template directories, loggers等資源時,這是非常方便的. 多實(shí)例化時,我們要用到 org.apache.velocity.app.VelocityEngine類. 下面是一個例子,請注意和上面singleton example同法時的不同:
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
...
/*
* create a new instance of the engine
*/
VelocityEngine ve = new VelocityEngine();
/*
* configure the engine. In this case, we are using
* ourselves as a logger (see logging examples..)
*/
ve.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this);
/*
* initialize the engine
*/
ve.init();
...
Template t = ve.getTemplate("foo.vm");
可以看到,這是非常簡單的直接使用就行了.無論用singleton 或 separate instances都不需要改變你程序的上層結(jié)構(gòu)及模板內(nèi)容.
對于開發(fā)人員而言, 你應(yīng)在以下兩個類中二者擇一的使用
org.apache.velocity.app.Velocity 應(yīng)用于 singleton model, org.apache.velocity.app.VelocityEngine一般用于non-singleton model (‘separate instance‘).
‘context‘ 是Velocity中的一個核心概念, 這是一個從系統(tǒng)的”數(shù)據(jù)容器(a container of data)”引出的一個常見概念. 這里的context在java程序?qū)雍湍0逡晥D層(template layer ( or the designer ))之間扮演著一個”數(shù)據(jù)對象傳送者”‘(carrier‘)的角色.
做為程序員,你可以將你程序生成的不同類型的數(shù)據(jù)對象放入context中,對于視圖設(shè)計(jì)來說,這些對象(包含它們的數(shù)據(jù)域和命令)將在模板元素中被引用到(references)。一般來說,你將和視圖設(shè)計(jì)者一起決定應(yīng)用需要哪些數(shù)據(jù),可以說,你放入context中的數(shù)據(jù)對象在這里成為一種”API”,由視圖設(shè)計(jì)者在模板中來訪問.因此,在向context中決定放放哪些數(shù)據(jù)對象時,程序的設(shè)計(jì)者需要仔細(xì)分析視圖表現(xiàn)所需的數(shù)據(jù)內(nèi)容。
雖然Velocity中你可以創(chuàng)建自己的Context類來支持一些個性化的應(yīng)用(比如,一個訪問,保存LDAP Server服務(wù)的context),你可以實(shí)現(xiàn)VelocityContext這個己封裝較為完務(wù)的基類。
VelocityContext對象基本上可滿足大多的應(yīng)用, 我們強(qiáng)烈建議你除非在特別的情況下,否則不要創(chuàng)建自己的Context實(shí)現(xiàn)!
VelocityContext用法十分簡單,類似于Hashtable class.下面是這個接口提供的兩個基本用法:
public Object put(String key, Object value);
public Object get(String key);
很像Hashtable吧,這里的value必須是一個java.lang.Object類(不能是原始類型,像int,boolean), 也不能是null值. 原始類型(Fundamental types like int or float)必須被包裝為一個適當(dāng)對應(yīng)的Object型.
OK,以上就是context 對象的用法概念,很簡單我們卻哆嗦這么:). 關(guān)于其更多的介紹,請見API documentation.
在放入context前,你對對象有著全面的操作自由. 但就像所有的自由一樣, 你必須遵守一些規(guī)則,承擔(dān)一些責(zé)任,因此,你必須理解Velocity是如何使用對象的,Velocity的VTL支持多種類型的集合類型(collection types) 使用#foreach().
- Object [] 一般對象數(shù)組. Velocity將內(nèi)功能會將它包裝成功之為一個實(shí)現(xiàn)Iterator interface對象, 這個轉(zhuǎn)換是不需要程序員或視圖設(shè)計(jì)者參與.
- java.util.Collection :Velocity會使用他們的標(biāo)準(zhǔn)iterator() 得到一個可以迭代中使用的 Iterator對象,如果你使用自己的實(shí)現(xiàn)了Collection interface的對象,要確保它的iterator() 命令返回一個可用的Iterator.
- java.util.Map接口對象,Velocity 使用其頂層接口的values() 命令得到一個實(shí)現(xiàn) Collection interface的對象, 應(yīng)用其iterator()再返回一個Iterator.
- java.util.Iterator使用特別注意 : 如果一個Iterator對象被放置到context中,當(dāng)在模板中有多個 #foreach()指令中,這些#foreach() 將順序執(zhí)行,如果第一個調(diào)用失敗,后面的將阻塞且不能重置.
- java.util.Enumeration USE WITH CAUTION : 如同java.util.Iterator一樣的道理,Velocity將使用的是一個不能重置(‘non-resettablity‘)或者說一個final型的對象.
因此,僅當(dāng)在不得己的情況下,Iterator and Enumeration 對象才有必要放入context中---也許你有更好的辦法不使用他們.
例如,你可以將如下代碼:
Vector v = new Vector();
v.addElement("Hello");
v.addElement("There");
context.put("words", v.iterator() );
替換為:
context.put("words", v );
另外一個新引入的概念是context chaining.有時也叫做context wrapping(有點(diǎn)類似與servlet中的chain), 這個高級特性讓你可以連結(jié)多個獨(dú)立的Velocity的contexts,以便在template中使用.
以下是這種用法的代碼示例 :
VelocityContext context1 = new VelocityContext();
context1.put("name","Velocity");
context1.put("project", "Jakarta");
context1.put("duplicate", "I am in context1");
VelocityContext context2 = new VelocityContext( context1 );
context2.put("lang", "Java" );
context2.put("duplicate", "I am in context2");
template.merge( context2, writer );
在上面的代碼中, context2 做為context1的chains. 這意味著你在模板中可以使用放入這兩個context中的任何一個對象, 當(dāng)兩個context中有相同的key中,在模板中輸出時,將會輸出最后一個key的值,如上例key為duplicate的值將輸出為 "I am in context2".
其實(shí),在上例中不存在duplication, 或‘covering‘, context1中的string "I am in context1" 依然可以通過context1.get("duplicate")方法得到. 但在上例中,模板中引用 ‘$duplicate‘ 將會返回 ‘I am in context2‘, 而且模板不能再訪問到context1中的‘I am in context1‘.
另外要注意的是,當(dāng)你嘗試在模板中加入信息,比如使用#set()聲明,這將對所己輸出的模板產(chǎn)生影響.
如前所述, Velocity context類也是或擴(kuò)展的, 但在這份指南中沒有述及. 如果你有興趣,可以查看org.apache.velocity.context 中的代碼以了解contexts 是如何生成,java數(shù)據(jù)對象以何機(jī)制傳出的. 例程 examples/context_example有一些示例展現(xiàn).
Java代碼中的數(shù)據(jù)對象與模板交互有兩種常見方式:
模板設(shè)計(jì)者從模板中執(zhí)行程序員放入到context中的java對象的命令:
#set($myarr = ["a","b","c"] )
$foo.bar( $myarr )
當(dāng)模板加入一個對象到context中,模板合并輸出后,java代碼將可以訪問這些對象.
#set($myarr = ["a","b","c"] )
#set( $foo = 1 )
#set( $bar = "bar")
這里述及這些技巧有些過早,但現(xiàn)在必須理解以下概念:
- The VTL通過context或method所傳的 [ 1..10 ] and ObjectArray ["a","b"] 是java.util.ArrayList對象. 因此你的對象的命令設(shè)計(jì)時,要具有兼容性.
- Numbers在context中將被包裝為Integers, strings,當(dāng)然就是Strings了.
- Velocity會適當(dāng)?shù)母鶕?jù)調(diào)用的參數(shù)類型適配對象的調(diào)用命令, setFoo( int i )將一個 int 放入context 和 #set()是不會沖突的.
每一個VelocityContext(或任意源自AbstractContext)的對象,都是一個封裝好指定規(guī)則的的存儲節(jié)點(diǎn),對于一般開發(fā)都來說,只需使用就是.但這里還有一些你應(yīng)知道的特性:
考慮以下情況:
- 你的模板重復(fù)使用VelocityContext object.
- Template caching is off.
- 反復(fù)調(diào)用getTemplate() 命令.
這都有可能引起 VelocityContext的內(nèi)存泄露( ‘leak‘ memory )---當(dāng)它匯集過多的數(shù)據(jù)對象時,因此強(qiáng)烈建議你做到以下幾點(diǎn) :
- 在每個模板渲染過種中(template render process)創(chuàng)建一個新的VelocityContext. 這會防止過多的cache data. 當(dāng)需要重用一個 VelocityContext 因?yàn)樗鼉?nèi)部己放置了數(shù)據(jù)對象, 你只需要像這樣簡單的包裝一下:VelocityContext useThis = new VelocityContext( populatedVC );具體可以參看 Context chaining 獲取更多信息.
- 打開模板的caching功能. 以防止重復(fù)解析模板,當(dāng)然,這會要求服務(wù)器有更高的性能.
在迭代操作時,要重用模板對象. 這樣將不會對Velocity造成過大壓力, 如果緩存關(guān)閉, 就需要每次都讀取和解析模板, 導(dǎo)致 VelocityContext 中每次都要保存大量新的信息.
Velocity最通常用在servlet中做為www服務(wù). 有非常多的理由告訴你這項(xiàng)任務(wù)是最適合Velocity完成的,最重要的一個就是Velocity‘s可以分離視圖(表現(xiàn)層和)代碼層.在這里可以看到更多的理由this.
在servlet中使用Velocity是非常簡單的. 你只需要extend 己有的 VelocityServlet class和一個必須實(shí)現(xiàn)的方法: handleRequest().
public Template handleRequest( HttpServletRequest, HttpServletResponse, Context )
這個方法直接傳送HttpServletRequest 和 HttpServletResponse objects,. 這個方法可以返回null值表示所有處理己經(jīng)完成, 相對而言,指示velocity調(diào)用velocity requestCleanup()更為常用. 如下代碼示例(在例程中有)
public class SampleServlet extends VelocityServlet
{
public Template handleRequest( HttpServletRequest request,
HttpServletResponse response,
Context context )
{
String p1 = "Jakarta";
String p2 = "Velocity";
Vector vec = new Vector();
vec.addElement( p1 );
vec.addElement( p2 );
context.put("list", vec );
Template template = null;
try
{
template = getTemplate("sample.vm");
}
catch( ResourceNotFoundException rnfe )
{
// couldn‘t find the template
}
catch( ParseErrorException pee )
{
// syntax error : problem parsing the template
}
catch( Exception e )
{}
return template;
}
}
看起來好熟悉吧? 除過處理一些異常,基本的功能Velocity都己為你準(zhǔn)備好了, 就連在應(yīng)用程序中要你寫的merge() 這一步,VelocityServlet自己也己處理,這就是基本的用法:取得context, 加入我們自己的對象最后返回模板 template.
默認(rèn)的 Context是作為 handleRequest() 傳入的. 可以使用以下常量直接訪問 request和response對象,VelocityServlet.REQUEST (value = ‘req‘) and VelocityServlet.RESPONSE (value = ‘res‘) ,如下是java例程 :
public Template handleRequest( Context context )
{
HttpServletRequest request = (HttpServletRequest) context.get( REQUEST );
HttpServletResponse response = (HttpServletResponse) context.get( RESPONSE );
...
}
可以在模板中如下訪問:
#set($name = $req.getParameter(‘name‘) )
一些更高級的用法,如VelocityServlet base class 可以在處理請求時重寫更多的簽名方法 :
Properties loadConfiguration( ServletConfig )
這可以重寫常規(guī)的配置方法. 這常用在修改日志路徑或運(yùn)行時改寫webapp root的絕對路徑.
你可以創(chuàng)建自己的Context object. 這可以使用更高級的技術(shù), 如數(shù)據(jù)鏈或預(yù)載數(shù)據(jù)和工具類. 默認(rèn)的實(shí)理僅返回一個己內(nèi)置request and response 對象的VelocityContext.你直以在模板中直接訪問他們的命令。
void setContentType( HttpServletRequest,HttpServletResponse )
你可以自己定義contentType, 或提取client定義的. 默認(rèn)的contentType類型在velocity.properties文件中配置, 一般來說,默認(rèn)的"text/html" 類型是不夠詳細(xì)的.
void mergeTemplate( Template, Context, HttpServletResponse )
你可以生成輸出流(output stream). VelocityServlet使用含有多種輸出對象的池, 在特定情形下,重寫這個命令是有用的.
這個調(diào)用一般在處理完后做資源的清理工作,如有需要,在這個命令的重寫內(nèi)容中加上你的代碼.
在處理中,當(dāng)錯誤發(fā)生時,這個方法會被調(diào)用. 默認(rèn)的實(shí)現(xiàn)是將發(fā)送一個簡單的HTML格式的錯誤內(nèi)容到客戶端. 你可以重寫以定制錯誤處理.
發(fā)布基于Velocity的servlets 時,需要參數(shù)來配置 Velocity runtime. 在Tomcat上, 一個簡單的方法是將velocity.properties文件放到你的webApp root目錄下 (webapps/appname ) 然后在 WEB-INF/web.xml 文件中加上以下幾行 :
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.foo.bar.MyServlet</servlet-class>
<init-param>
<param-name>properties</param-name>
<param-value>/velocity.properties</param-value>
</init-param>
</servlet>
以上配置將確保MyServlet 可以被載入,Velocity也將會使velocity.properties來初始化.
注意: Velocity 在運(yùn)行時使用的是單實(shí)例模式, 因此將 velocity-XX.jar放到Y(jié)ourWebAPP/WEB-INF/lib目錄即可. 但當(dāng)多個webApp要使用時,放入CLASSPATH 或Servlet容器的頂層 lib是最好的選擇.
Velocity被設(shè)計(jì)為一個通用的功具包,它也常用于一般的應(yīng)用程序中. 如這篇指南開始所討論的那樣, 另外,這里還有一些對一般應(yīng)用程序來說很有用的工具類:
Velocity 提供一個 utility class ( org.apache.velocity.app.Velocity ). 這個類中提共一個初始化Velocity所必需的方法, 具體的細(xì)節(jié)可以到文檔中查看.
Velocity runtime engine在運(yùn)行時是單實(shí)便模式,它可以給 Velocity的用戶通過同一個jvm提供日志,資源訪問方法. 這個engine運(yùn)行時僅初始化一次. 當(dāng)然,你可以嘗初對init()進(jìn)行多次調(diào)用, 但只有第一次有效. The Velocity utility class 目前提供5個命令來配置運(yùn)行時engine:
這5個配置方法是 :
- setProperty( String key, Object o )
這么簡單,不用解釋了吧.
- Object getProperty( String key )
嗯,也不說了
- init()
用默認(rèn)的參數(shù)文件初始化
- init( Properties p )
用一個特定的java.util.Properties對象初始化.
- init( String filename )
用指定名字的參數(shù)文件初始化
注意:除非你指定了,否則初始時會使用默認(rèn)參數(shù). 僅當(dāng)在調(diào)用init()時的配置會對系統(tǒng)生效。
通用的 initializing Velocity 一般步驟如下:
- 設(shè)置你指定的配置參數(shù)值在文件org/apache/velocity/runtime/defaults/velocity.properties中,或放入java.util.Properties, 在第一次調(diào)用init( filename )或 init( Properties ) .
- 單獨(dú)設(shè)置每個配置參數(shù)通過調(diào)用 setProperty(),最后調(diào)用init(). 這適合一些一有自己的CMS系統(tǒng)(configuration management system )程序。
運(yùn)行時一但初始化, 你就可以開始工作了.. 只需要考慮組裝什么樣的數(shù)據(jù)對象到你的輸出模板上, Velocity utility class 可以幫你更容易做到這些.這里有一些命令摘要描述如下 :
- evaluate( Context context, Writer out, String logTag, String instring )
evaluate( Context context, Writer writer, String logTag, InputStream instream )
這個命令用來修飾輸入流,你可以將包含TVL的模板文件從內(nèi)置的String對象、DB、或非文件系統(tǒng)的數(shù)據(jù)源輸入,.
- invokeVelocimacro( String vmName, String namespace, String params[], Context context, Writer writer )
你可以直接訪問Velocimacros. 也可以通過evaluate()命令來完成,這里你只需簡單的傳入VM文件名(模板文件), 創(chuàng)建一組VM參數(shù)放到Context,然后輸出. 注意放入Context的參數(shù)必須是鍵-值對出現(xiàn)的.
- mergeTemplate( String templateName, Context context, Writer writer )
這個命令用來執(zhí)行Velocity的模板合并和渲染功能. 它將應(yīng)用context中的數(shù)據(jù)對象到指定文件名的模板中.將結(jié)果輸出的指定的Writer. 當(dāng)然,如果沒有特別的需要,不建議這么做.
- boolean templateExists( String name )
檢測當(dāng)前配置的資源中,是否存在name的模板名.
這樣,我們就更容易編寫使用Velocity的java代碼了. Here it is
import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;
public class Example2
{
public static void main( String args[] )
{
/* first, we init the runtime engine. Defaults are fine. */
Velocity.init();
/* lets make a Context and put data into it */
VelocityContext context = new VelocityContext();
context.put("name", "Velocity");
context.put("project", "Jakarta");
/* lets render a template */
StringWriter w = new StringWriter();
Velocity.mergeTemplate("testtemplate.vm", context, w );
System.out.println(" template : " + w );
/* lets make our own string to render */
String s = "We are using $project $name to render this.";
w = new StringWriter();
Velocity.evaluate( context, w, "mystring", s );
System.out.println(" string : " + w );
}
}
運(yùn)行如上程序,在運(yùn)行程序的目錄下將得到模板文件testtemplate.vm (默認(rèn)配置中模板文件的輸出位置是當(dāng)前目錄):
template : Hi! This Velocity from the Jakarta project.
string : We are using Jakarta Velocity to render this.
where the template we used, testtemplate.vm, is
Hi! This $name from the $project project.
在這里,我們不得不使用 mergeTemplate() and evaluate() in our program. 這主要是為了示例. 在一般應(yīng)用中,這是很少使用到的,但我們提供了根據(jù)具體需要自由使用的途徑.
這有點(diǎn)不和我們這篇文張本意的“基礎(chǔ)功能指南”這一意圖, 但我想這是必須知道的. 首先, 你得到一個放置了數(shù)據(jù)對象的context對象, 不用的是使用了命令 mergeTemplate(), mergeTemplate() 所做的工作是合并模板, 在運(yùn)行時調(diào)用低層功能(lower-level). 接下來,通過evaluate()方法使用一個String動態(tài)生成模板.
這是同樣簡單的使用Velocity engine的方式,這些可選功能也許可以幫你做一些重復(fù)的工作,比如生成模板的模板:)
There are three exceptions that Velocity will throw during the parse / merge cycle在解析/合并(parse/merge)模板周期中,Velocity或能出現(xiàn)三個Velocity異常.另外,還可能會有 IO problems, etc. Velocity自定義的異??梢栽趐ackage org.apache.velocity.exception:
- ResourceNotFoundException
當(dāng)velocity的資源管理器無法找到系統(tǒng)請求的資源時出現(xiàn).
- ParseErrorException
當(dāng)parse模板文件中的 VTL 語法出錯時出現(xiàn).
- MethodInvocationException
Thrown when a method of object in the context thrown an exception during render time,當(dāng)處理模板中,context中的對象的命令調(diào)用出錯時.
當(dāng)然,每一次出錯時,相關(guān)的消息內(nèi)容會保存到運(yùn)行時的日志文件中,獲取更多資料,請查看API文檔。
在以上的一些例程中,使用默認(rèn)的properties配置你的程序非常方便. 但你可以根據(jù)自己的需要,用自己的配置文件通過在Velocity中調(diào)用init(String yourFileName)命令傳入你的配置文件名, 或創(chuàng)建一個保存了你的配置參數(shù)的 java.util.Properties對象,通過調(diào)用 init(Properties)命令來實(shí)現(xiàn). 這其中后一個方式是很便捷的, 你可以直接將一個獨(dú)立的參數(shù)文件通過load()調(diào)用,將其中的配置參數(shù)置入Properties對象中. 你還可以做得列好---你可以在運(yùn)行時運(yùn)態(tài)的從你的程序框架中加入?yún)?shù). 這樣,你應(yīng)可以將Velocity的配置參數(shù)和你原來應(yīng)用的配置在不做大的更改情況下共用。
當(dāng)需要從一個指定的目錄提取模板文件時(默認(rèn)為當(dāng)前目錄),你可以這樣做 :
import java.util.Properties;
...
public static void main( String args[] )
{
/* first, we init the runtime engine. */
Properties p = new Properties();
p.setProperty("file.resource.loader.path", "/opt/templates");
Velocity.init( p );
/* lets make a Context and put data into it */
...
這樣,Velocity在需要時,將自動到/opt/templates目錄下查找模板文件,如果有問題可以查看velocity.log中所記錄的出詳細(xì)出錯信息。它會幫你有效的消息錯誤.
Application Attributes (應(yīng)用程序?qū)傩?是和VelocityEngine 的運(yùn)行時實(shí)例(Runtimeinstance)相關(guān)聯(lián)的,名-值對(name-value pairs)格式的參數(shù),可用來存運(yùn)RuntimeInstance時的信息.
設(shè)計(jì)這個功能的目標(biāo)是Velocity程序需要與應(yīng)用層或用戶定制部分(如日志,資源,裝載器等)通信.
The Application Attribute API is very simple. From the application layer,在 VelocityEngine 和 Velocity classes 中都有如下命令:
public void setApplicationAttribute( Object key, Object value );
這里的key與value的設(shè)置沒有任何限制,可以在程序運(yùn)行中設(shè)置,這是不同與Velocity的init()調(diào)用的.
內(nèi)部程序可以通過接口 RuntimeServices 如下的命令來取得這些參數(shù):
public Object getApplicationAttribute( Object key ):
從Velocity1.1版本起,加入了良好的事件處理機(jī)制. 你可將自己的事件處理器類注冊給事件分發(fā)器(EventCartridge) , 事件分發(fā)器實(shí)際上扮演著Velocity engine在運(yùn)行時訪問事件處理器(event handlers)的一個代理的角色。目前,有3種Velocity系統(tǒng)定義的事件可以被處理,它們都位與包org.apache.velocity.app.event 中.
org.apache.velocity.app.event.NullSetEventHandler
當(dāng)模板中#set() 指令關(guān)聯(lián)的是一個空值時, 這是很常見的一個問題. The NullSetEventHandler 事件處理器可以讓你決定如何處理,其接口代碼如下.
public interface NullSetEventHandler extends EventHandler
{
public boolean shouldLogOnNullSet( String lhs, String rhs );
}
org.apache.velocity.app.event.ReferenceInsertionEventHandler
A ReferenceInsertionEventHandler 這個事件處理器可以讓開發(fā)者在引用對象值如($foo)輸出到模板之前截取修改.
public interface ReferenceInsertionEventHandler extends EventHandler
{
public Object referenceInsert( String reference, Object value );
}
org.apache.velocity.app.event.MethodExceptionEventHandler
當(dāng)用戶數(shù)據(jù)對象中的某個命令出錯時, 實(shí)現(xiàn)了 MethodExceptionEventHandler接口的事件處理器將被調(diào)用以獲得具體的命令名字和Exception對象. 事件處理器也可以重組一個有效的對象返回(一般用于統(tǒng)一的異常處理), 或向它的父類throws一個new Exception, MethodInvocationException接口如下:
public interface MethodExceptionEventHandler extends EventHandler
{
public Object methodException( Class claz, String method, Exception e )
throws Exception;
}
可以直接使用一個事件分發(fā)器(EventCartridge),如下例程在org.apache.velocity.test.misc.Test中:
...
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
import org.apache.velocity.app.event.MethodExceptionEventHandler;
import org.apache.velocity.app.event.NullSetEventHandler;
...
public class Test implements ReferenceInsertionEventHandler,
NullSetEventHandler,
MethodExceptionEventHandler
{
public void myTest()
{
....
/*
* now, it‘s assumed that Test implements the correct methods to
* support the event handler interfaces. So to use them, first
* make a new cartridge
*/
EventCartridge ec = new EventCartridge();
/*
* then register this class as it contains the handlers
*/
ec.addEventHandler(this);
/*
* and then finally let it attach itself to the context
*/
ec.attachToContext( context );
/*
* now merge your template with the context as you normally
* do
*/
....
}
/*
* and now the implementations of the event handlers
*/
public Object referenceInsert( String reference, Object value )
{
/* do something with it */
return value;
}
public boolean shouldLogOnNullSet( String lhs, String rhs )
{
if ( /* whatever rule */ )
return false;
return true;
}
public Object methodException( Class claz, String method, Exception e )
throws Exception
{
if ( /* whatever rule */ )
return "I should have thrown";
throw e;
}
}
Velocity‘s runtime configuration is controlled by a set of configuration keys listed below Velocity的運(yùn)行時配置由一個key-value列表控制.
Velocity中有一些默認(rèn)的值在以下位置可以找到:
/src/java/org/apache/velocity/runtime/defaults/velocity.defaults,Velocity基礎(chǔ)配置,它會確保Velocity總是有一個“正常的”配置以便運(yùn)行.但它不一定是你想要的配置.
任何配置必須在 init()調(diào)用前發(fā)生才會在運(yùn)行時替換掉默認(rèn)的配置。這意味著你只需改變你需要的而不是所有的配置都要動.
以下,是默認(rèn)配置的說明:
runtime.log = velocity.log
用以指定Velocity運(yùn)行時日志文件的路勁和日志文件名,如不是全限定的絕對路徑,系統(tǒng)會認(rèn)為想對于當(dāng)前目錄.
runtime.log.logsystem
這個參數(shù)沒有默認(rèn)值,它可指定一個實(shí)現(xiàn)了interface org.apache.velocity.runtime.log.LogSystem.的自定義日志處理對象給Velocity。這就方便將Velocity與你己有系統(tǒng)的日志機(jī)制統(tǒng)一起來.具體可見Configuring the Log System 這一節(jié).
runtime.log.logsystem.class = org.apache.velocity.runtime.log.AvalonLogSystem
上面這行,是一個示例來指定一個日志記錄器.
runtime.log.error.stacktrace = false
runtime.log.warn.stacktrace = false
runtime.log.info.stacktrace = false
這些是錯誤消息跟蹤的開關(guān).將會生成大量、詳細(xì)的日志內(nèi)容輸出.
runtime.log.invalid.references = true
當(dāng)一個引用無效時,打開日志輸出. 在生產(chǎn)系統(tǒng)運(yùn)行中,這很有效,也是很有用的調(diào)試工具.
input.encoding = ISO-8859-1
輸出模板的編碼方式 (templates). 你可選擇對你模板的編碼方式,如UTF-8.GBK.
output.encoding = ISO-8859-1
VelocityServlet 對輸出流(output streams)的編碼方式.
directive.foreach.counter.name = velocityCount
在模板中使用#foreach() 指令時,這里設(shè)定的字符串名字將做為context key 代表循環(huán)中的計(jì)數(shù)器名,如以上設(shè)定,在模板中可以通過 $velocityCount來訪問.
directive.foreach.counter.initial.value = 1
#foreach() 中計(jì)數(shù)器的起始值.
directive.include.output.errormsg.start =
directive.include.output.errormsg.end =
使用#include()時,定義內(nèi)部流中開始和結(jié)束的錯誤消息標(biāo)記,如果兩者都設(shè)這屯,錯誤消息將被輸出到流中‘.但必須是兩都定義.
directive.parse.maxdepth = 10
定義模板的解析深度,當(dāng)在一個模板中用#parse()指示解析另外一個模板時,這個值可以防止解析時出現(xiàn)recursion解析.
resource.manager.logwhenfound = true
定義日志中的 ‘found‘ 務(wù)目開關(guān).當(dāng)打開時,如Resource Manager第一次發(fā)現(xiàn)某個資源時, the first time, the resource name and classname of the loader that found it will be noted in the runtime log.
resource.loader = <name> (default = File)
Multi-valued key. Will accept CSV for value. (可以有多個以.號分開的值),可以理解為指定資源文件的擴(kuò)展名.
<name>.loader.description = Velocity File Resource Loader
描述資源裝載器名字.
<name>.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
實(shí)現(xiàn)的資源裝載器的類名. 默認(rèn)的是文件裝載器.
<name>.resource.loader.path = .
Multi-valued key. Will accept CSV for value. 資源位置的根目錄. 當(dāng)前配置會使用FileResourceLoader and JarResourceLoader 遍歷目錄下的所有文件以查找資源.
<name>.resource.loader.cache = false
控制裝載器是否對文件進(jìn)行緩存.默認(rèn)不存是為了方便開發(fā)和調(diào)試. 在生產(chǎn)環(huán)境布署(production deployment)時可設(shè)為true以提高性能, 這里參數(shù) modificationCheckInterval應(yīng)設(shè)為一個有效值—以決定多久reload一次.
<name>.resource.loader.modificationCheckInterval = 2
當(dāng)模把caching打開時,這個以秒為單位的值指示系統(tǒng)多久檢測一次模板是否己修改以決定是否需要,如果設(shè)為 <= 0, Velocity將不做檢測.
FileResourceLoader 的默認(rèn)參數(shù)完整示例:
resource.loader = file
file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = .
file.resource.loader.cache = false
file.resource.loader.modificationCheckInterval = 2
velocimacro.library = VM_global_library.vm
Multi-valued key. Will accept CSV for value.當(dāng)Velocity engine運(yùn)行時,要被載入的含有宏代碼庫的文件名. 所有模板都可訪問宏(Velocimacros ). 這個文件位置在相對于資源文件的根目錄下.
velocimacro.permissions.allow.inline = true
Determines of the definition of new Velocimacros via the #macro() directive in templates is allowed,定義在模板中是否可用#macro()指令定義一個新的宏. 默認(rèn)為true,意味所有模板都可定義new Velocimacros. 注意,這個設(shè)定,如模板中的有可能替換掉全局的宏定義.
velocimacro.permissions.allow.inline.to.replace.global = false
控制用戶定義的宏是否可以可以替換Velocity的宏庫.
velocimacro.permissions.allow.inline.local.scope = false
控制模板中的宏的專有命名空間.When true, 一個模板中的 #macro() directive 只能被定義它的模板訪問. 這意味者所有的宏都不能共想了,當(dāng)然也不會互想擾亂、替換了.
velocimacro.context.localscope = false控制Velocimacro 的引用訪問(set/get)是涉及到Context范圍還是僅在當(dāng)前的Velocimacro中.
velocimacro.library.autoreload = false
控制宏庫是否自動載入. 設(shè)為true時,源始的Velocimacro 將根據(jù)是否修改過而決定是否需要reLoad,可在調(diào)試時很方便,不需重啟你的服務(wù)器,如用參數(shù) file.resource.loader.cache = false的設(shè)置一樣,主要是為方便開發(fā)調(diào)試用.
runtime.interpolate.string.literals = true
Controls interpolation mechanism of VTL String Literals. Note that a VTL StringLiteral is specifically a string using double quotes that is used in a #set() statement, a method call of a reference, a parameter to a VM, or as an argument to a VTL directive in general. See the VTL reference for further information.
parser.pool.size = 20
控制Velocity啟動是需要創(chuàng)建并放到池中預(yù)備使用的模板解析器的個數(shù)----這只是預(yù)裝. 默認(rèn)的20個對一般用戶來說足夠了. 即使這個值小了,Velocity也會運(yùn)行時根據(jù)系統(tǒng)需要動態(tài)增加(但增加的不會裝入池中). 新增時會在日志中輸出信息
Velocity有很容易擴(kuò)展的日志系統(tǒng).即使不做任何設(shè)置,velocity也會將日志輸出到當(dāng)前目錄下的 velocity.log文件中. 對一些高級用戶, 可以很方便的將你當(dāng)前系統(tǒng)的日志和它整合起來.
- Default Configuration
默認(rèn)在當(dāng)前目錄下創(chuàng)建日志文件.
- Existing Log4j Category
從1.3開始, Velocity 可以將日志輸出到Log4j配置中. 但你必須:
1. 確認(rèn)Log4j jar is in your classpath. (你應(yīng)一直這樣做,自從使用Velocity.)
2. 配置Velocit使用SimpleLog4JLogSystem class.
3. 通過 ‘runtime.log.logsystem.log4j.category‘ 參數(shù)指定日志條目名字.
這里不建議使用老的Log4JLogSystem class. 可以在隨后看到示例.
- Custom Standalone Logger
你可以創(chuàng)建定制的日志類 – 你只需簡單的實(shí)現(xiàn)接口org.apache.velocity.runtime.log.LogSystem然后將你的實(shí)現(xiàn)類名配置到運(yùn)行時參數(shù) runtime.log.logsystem.class的值, Velocity在init()時將創(chuàng)建你的日志實(shí)例. 更多相關(guān)信息可以看 Velocity helper class 和 configuration keys and values. 要注意的是, 接口org.apache.velocity.runtime.log.LogSystem 在1.2后才支持這一功能.
- Integrated Logging
你可以將Velocity的日志和你現(xiàn)存系統(tǒng)的日志整合到一起.
這里是一個使用Log4j做為Velocity日志的例子.
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.log4j.Category;
import org.apache.log4j.BasicConfigurator;
public class Log4jCategoryExample
{
public static String CATEGORY_NAME = "velexample";
public static void main( String args[] )
throws Exception
{
/*
* configure log4j to log to console
*/
BasicConfigurator.configure();
Category log = Category.getInstance( CATEGORY_NAME );
log.info("Hello from Log4jCategoryExample - ready to start velocity");
/*
* now create a new VelocityEngine instance, and
* configure it to use the category
*/
VelocityEngine ve = new VelocityEngine();
ve.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
"org.apache.velocity.runtime.log.SimpleLog4JLogSystem" );
ve.setProperty("runtime.log.logsystem.log4j.category", CATEGORY_NAME);
ve.init();
log.info("this should follow the initialization output from velocity");
}
}
上面的例子可以在examples/logger_example.下找到.
這是一個定制實(shí)現(xiàn)你自己的日志記錄器,并將其加入到Velocity的日志系統(tǒng)中. LogSystem interface—只需要支持這個接口.
import org.apache.velocity.runtime.log.LogSystem;
import org.apache.velocity.runtime.RuntimeServices;
...
public class MyClass implements LogSystem
{
...
public MyClass()
{
...
try
{
/*
* register this class as a logger
*/
Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, this );
Velocity.init();
}
catch (Exception e)
{
/*
* do something
*/
}
}
/**
* This init() will be invoked once by the LogManager
* to give you current RuntimeServices intance
*/
public void init( RuntimeServices rsvc )
{
// do nothing
}
/**
* This is the method that you implement for Velocity to call
* with log messages.
*/
public void logVelocityMessage(int level, String message)
{
/* do something useful */
}
...
}
Velocity一個非常重要的基礎(chǔ)功能是資源管理和裝載. 這里資源 ‘resources‘ 不僅包括了模板(‘templates‘),RMS也可以處理非模板文件, 特別是在使用 #include() 指令時.
resource loader system (資源裝載系統(tǒng))很容易擴(kuò)展,可以同時執(zhí)行多個資源裝載器的操作. 這極大的方便了資源管理, --你可以根據(jù)需要,定制自己的資源裝載器.
Velocity當(dāng)前包含4種資源管理器, 說明如下:(注意例程中的配置參數(shù)有一個loader配置名 (ex.‘file‘ in file.resource.loader.path).這個 ‘common name‘ 配置不一定會在你的系統(tǒng)中工作. 具體可見 resource configuration properties 理解系統(tǒng)如何工作. 這每一個loader都在包 org.apache.velocity.runtime.resource.loader. 中
- FileResourceLoader : 這個loader從文件系統(tǒng)取得資源,其配置參數(shù)如下:
- file.resource.loader.path = <path to root of templates>
- file.resource.loader.cache = true/false
- file.resource.loader.modificationCheckInterval = <seconds between checks>
- JarResourceLoader : 這個loader可以從jar文件包中取得資源,在你把你的模板文件全部打包成 jar包時,系統(tǒng)會用這個loader來提取. 配置基本一樣除過jar.resource.loader.path, 這里或以使用標(biāo)準(zhǔn)的JAR URL syntax of java.net.JarURLConnection.
- ClasspathResourceLoader : 從classloader中取得資源. 一般來說,這意味著ClasspathResourceLoader將從classpath中load templates.這是在Servlet類型應(yīng)用常見的一種設(shè)置。支持Servlet 2.2 (或更新)規(guī)范的容器Tomcat就是這樣一個例子. 這種裝載方式很有效, 因此你必須將你的模板打成jar包放到你的web應(yīng)用的WEB-INF/lib 目錄下.就不再存在絕對、相對路徑的問題了,與以上兩個裝載器相比 ClasspathResourceLoader不僅在servlet container中用也,幾乎所有應(yīng)用的上下文(context)都有用.
- DataSourceResourceLoader : 這個loader可以從數(shù)據(jù)庫載入資源. 這個loader不是標(biāo)準(zhǔn)j2EE的一部分,因此需要取得J2EE 發(fā)行庫,將j2ee.jar加入到build/lib目錄下,然后編譯新的Velocity.jar設(shè)置ant target為jar-j2ee,更細(xì)說明請見文檔中對類 org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader的介紹.
第一就是要配置loader的名字. 參數(shù)resource.loader的值可以是你喜歡的用來關(guān)聯(lián)指定loader的名字.
resource.loader = file
下一步就是設(shè)置這個名字對應(yīng)的class了,這是最重要的一步 :
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
這個例子中,我們告訴Velocity我們設(shè)置的loader名字叫file,指定的類是org.apache.velocity.runtime.resource.loader.FileResourceLoader.下一步就是設(shè)置這個loader的一些重要參數(shù).
file.resource.loader.path = /opt/templates
file.resource.loader.cache = true
file.resource.loader.modificationCheckInterval = 2
這里,我們設(shè)置了查找模板的路徑是 /opt/templates. 然后打開caching,最后,設(shè)置檢測周期為2秒,以便Velocity檢測新的或己更改過的模板來load.
上示是一些基本配置,隨后,還會再有一些示例.
Do-nothing Default Configuration : 你也可以什么都不改動,就用默認(rèn)的配置. 這是默認(rèn)的loader配置:
resource.loader = file
file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = .
file.resource.loader.cache = false
file.resource.loader.modificationCheckInterval = 0
Multiple Template Path Configuration :多模板路徑配置如下所示,只要用逗號分開就是 :
resource.loader = file
file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = /opt/directory1, /opt/directory2
file.resource.loader.cache = true
file.resource.loader.modificationCheckInterval = 10
Multiple Loader Configuration : 多個loader配置,嗯,也很簡單,不說了,看例子就是.
#
# specify three resource loaders to use
#
resource.loader = file, class, jar
#
# for the loader we call ‘file‘, set the FileResourceLoader as the
# class to use, turn off caching, and use 3 directories for templates
#
file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = templatedirectory1, anotherdirectory, foo/bar
file.resource.loader.cache = false
file.resource.loader.modificationCheckInterval = 0
#
# for the loader we call ‘class‘, use the ClasspathResourceLoader
#
class.resource.loader.description = Velocity Classpath Resource Loader
class.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
#
# and finally, for the loader we call ‘jar‘, use the JarResourceLoader
# and specify two jars to load from
#
jar.resource.loader.description = Velocity Jar Resource Loader
jar.resource.loader.class = org.apache.velocity.runtime.resource.loader.JarResourceLoader
jar.resource.loader.path = jar:file:/myjarplace/myjar.jar, jar:file:/myjarplace/myjar2.jar
只是注意: ‘file‘, ‘class‘, and ‘jar‘ 這三個名字不是固定是,可以根據(jù)你的喜好來設(shè)定. 但只要保持上面的對應(yīng)關(guān)系就是.
資源管理器是相關(guān)資源 (template and static content)管理系統(tǒng)的核心部分, 它為應(yīng)用程序取得請求模板,查找他們的有效資源loaders,操作caching.對于高級用戶,可以用自定制的caching系統(tǒng)取代這個默認(rèn)的實(shí)現(xiàn).
資源管理器必須實(shí)現(xiàn) org.apache.velocity.runtime.resource.ResourceManager interface. 具體描述請看api文檔. 盡量使用默認(rèn)實(shí)現(xiàn),除非你認(rèn)為有必要在以下參數(shù)中換成你自己的 :
resource.manager.class
這個參數(shù)也可通過RuntimeConstants.RESOURCE_MANAGER_CLASS 設(shè)定。
資源的caching必須實(shí)現(xiàn) org.apache.velocity.runtime.resource.ResourceCache interface 接口,配置到參數(shù)中是 :
resource.manager.cache.class
這個參數(shù)也可通過 RuntimeConstants.RESOURCE_MANAGER_CACHE_CLASS 設(shè)定
從版本1.1開始, 可以設(shè)定資源的編解碼類型. 在API中也可以傳入解碼的方式 :
org.apache.velocity.servlet.VelocityServlet :
public Template getTemplate( String template, String encoding )
org.apache.velocity.app.Velocity :
public static Template getTemplate(String name, String encoding)
public static boolean mergeTemplate( String templateName, String encoding, Context context, Writer writer )
encoding 參數(shù)可以設(shè)定為JVM支持的某個值 "UTF-8" or "ISO-8859-1".關(guān)于字符集正式的名字, see here.
注意,這僅僅是編碼了模板自己 – 輸出的編碼由應(yīng)用程序指定.
Velocity‘s的VTL( velocity template language)處理XML數(shù)據(jù)很方便. Anakia是一個用XSL從XML中輸出視圖的例子. Velocity站點(diǎn),文檔包括 Jakarta site is also rendered using Anakia.
一般來說,處理XML會用到 JDOM 這樣的東東將XML轉(zhuǎn)成java數(shù)據(jù)結(jié)構(gòu) ,如下例示是一個XML文檔 :
<?xml version="1.0"?>
<document>
<properties>
<title>Developer‘s Guide</title>
<author email="geirm@">Velocity Documentation Team</author>
</properties>
</document>
一小段處理的讀取XML的java程序如下:
...
SAXBuilder builder;
Document root = null;
try
{
builder = new SAXBuilder( "org.apache.xerces.parsers.SAXParser" );
root = builder.build("test.xml");
}
catch( Exception ee)
{}
VelocityContext vc = new VelocityContext();
vc.put("root", root );
...
(See the Anakia source for details on how to do this, or the Anakia example in the examples directory in the distribution.) 現(xiàn)在,在模板中應(yīng)用 :
<html>
<body>
The document title is
$root.getChild("document").getChild("properties").getChild("title").getText()
</body>
</html>
就像渲染一般模板那樣, 使用 Context 中的JDOM tree. 雖然這個例子看起來不漂亮, 但它展示了這樣做是多么容易.
One real advantage of styling XML data in Velocity is that you have access to any other object or data that the application provides. You aren‘t limited to just using the data present in the XML document. You may add anything you want to the context to provide additional information for your output, or provide tools to help make working with the XML data easier. Bob McWhirter‘s Werken Xpath is one such useful tool - an example of how it is used in Anakia can be found in org.apache.velocity.anakia.XPathTool.
One issue that arises with XML and Velocity is how to deal with XML entities. One technique is to combine the use of Velocimacros when you need to render an entity into the output stream :
## first, define the Velocimacro somewhere
#macro( xenc $sometext )$tools.escapeEntities($sometext)#end
## and use it as
#set( $sometext = " < " )
<text>#xenc($sometext)</text>
where the escapeEntities() is a method that does the escaping for you. Another trick would be to create an encoding utility that takes the context as a constructor parameter and only implements a method:
public String get(String key)
{
Object obj = context.get(key)
return (obj != null) ? Escape.getText( obj.toString() ) : "";
}
Put it into the context as "xenc". Then you can use it as :
<text>$xenc.sometext</text>
This takes advantage of Velocity‘s introspection process - it will try to call get("sometext") on the $xenc object in the Context - then the xenc object can then get the value from the Context, encode it, and return it.
Alternatively, since Velocity makes it easy to implement custom Context objects, you could implement your own context which always applies the encoding to any string returned. Be careful to avoid rendering the output of method calls directly, as they could return objects or strings (which might need encoding). Place them first into the context with a #set() directive and the use that, for example :
#set( $sometext = $jdomElement.getText() )
<text>$sometext</text>
The previous suggestions for dealing with XML entities came from Christoph Reck, an active participant in the Velocity community. We are very grateful for his [unknowing] contribution to this document, and hope his ideas weren‘t mangled too badly :)
開發(fā)中常見的問題解答.
在VTL中無法訪問到類的數(shù)據(jù)域
最簡單的原因是我們無法反射/內(nèi)省(introspect )這個對象.因?yàn)榫蚈OP來說,對象中要隱藏自己沒有必要外露的數(shù)據(jù)或命令.解決方法:包狀成publicly 命令反回它,保證它是公開訪問的. 當(dāng)然,你要保證能改動源文件, 否則,就要用工具來解析它. org.apache.velocity.app.FieldMethodizer是用來解析你的類的, 如下示例如何將一個public static fields 導(dǎo)出到模板中.假設(shè)你的類是 :
public class Foo
{
public static String PATH_ROOT = "/foo/bar";
....
}
你可這樣將它放入context中:
context.put("myfoo", new FieldMethodizer( new Foo() ) );
然后在模板中就可以java代碼的風(fēng)格來訪問 :
$myfoo.PATH_ROOT
如果你需要訪問public的非靜態(tài)域時(public non-static members)甚止是私有成員!那你就必須擴(kuò)展或重寫 FieldMethodizer 這個類----但你為什么要搞得這么復(fù)雜呢?
Velocity到哪里提取模板文件?
默認(rèn)的,不做任何配置更改的情況下,Velocity會在當(dāng)前目錄下或相對與當(dāng)前目錄(如‘foo/bar.vm‘)下查找.
Velocity對這些都是自動處理的. Velocity只記住它自己的一個root目錄,這個概念不同與多根目錄的文件系統(tǒng)(like - "C:\", "D:\", etc).
希望這個指南能幫助您出色的將velocity應(yīng)用到項(xiàng)目中. 請將您的意見反饋發(fā)送到mail lists.
布署本文中的Servlet例程
Servlet開發(fā)者經(jīng)常受到的一個打擊是將servlet放錯了地方---一切都是好的除此之外. 使用Tomcat 、 Resin i這樣的Servlet容器都可以運(yùn)行起我們的SampleServlet . SampleServlet.java 在目錄 examples/servlet_example 下. 雖然有些servlet engines (Resin, for example) 會自動將它編譯,但是為了學(xué)習(xí),還是你親自動手先將它編譯過.
Jakarta Tomcat
Jakarta Tomcat 的安裝就不多說了. ‘webapp‘ 目錄是tomcat默認(rèn)的查找它的web應(yīng)用的root.所以,以下是我們要做的:
- 首先,創(chuàng)建一個新的 ‘webapp‘ 暫時名叫 velexample 放到Tomcat的webapps 目錄下, 這個新的目錄結(jié)構(gòu)如下 :
velexample
velexample/WEB-INF
velexample/WEB-INF/lib
velexample/WEB-INF/classes
- 將Velocity jar 放到velexample/WEB-INF/lib下. (從1.2版本后,所有相關(guān)依賴包都打包在. velocity-dep-1.2.jar中),當(dāng)然,相關(guān)的依賴包也必須放到WEB-INF/lib下. 具體可以看 "Getting Started" and "Dependencies", 這兩節(jié)的介紹.
- 將編譯過的SampleServlet.class放到 velexample/WEB-INF/classes 下.
- 將sample.vm 放到目錄velexample 下.
- 現(xiàn)在就可以啟動servlet來訪問servlet了.
- 在Browser中輸出如下 :
http://localhost:8080/velexample/servlet/SampleServlet
如不能工作,則試下 :
http://<your computer‘s ip address>:8080/velexample/servlet/SampleServlet
- 看到輸出結(jié)果了嗎?.
Setting up the example servlet under Caucho Technology‘s Resin servlet engine is also very simple. The following instructions were tested with the version 1.2.5 release. The following assume that you have unzip-ed or untar-ed the distribution, know how to start the servlet engine (something like bin/httpd.sh under unix...), and know where the doc directory is (in the root of the distribution).
- Copy the SampleServlet.class file into the doc/WEB-INF/classes directory.
- Copy the sample.vm template file into the doc/ directory
- Copy the Velocity jar (and any required dependencies - see note above in Tomcat setup section) into the doc/WEB-INF/lib directory.
- Start resin.
- To access the servlet, point your web browser at :
http://localhost:8080/servlet/SampleServlet
or if that doesn‘t work :
http://<your computer‘s ip address>:8080/servlet/SampleServlet
and you should see the output.
Note that this appeared to be a simpler configuration - with the Tomcat example, you set up a complete, new, separate webapp, whereas the Resin instructions don‘t, although both should provide a sufficient setup to let you play with Velocity.
Note that while we wish we could, we can‘t answer questions about the servlet engines. Please use the resources provided by the servlet engine provider.
Paw Dybdahl <pdy@csg.csc.dk> contributed this description of using Velocity with WebLogic, as well as some good general suggestions and examples for working with servlets.