Velocity開發者指南
(學習筆記)
一、Velocity 的工作原理
基本模式
當我們在Application或Servlet(實際上包括其他任何形式)中使用Velocity時,通常會做如下幾件事:
·初始化Velocity。適用于Velocity的兩種應用模式-單例(Singleton)和(separate runtime instance),并且你僅需要執行一次。
·創建一個Context對象。
·向Context對象添加數據。
·選擇一個模板。
·合并模板和數據并輸出。
通過org.apache.velocity.app.Velocity類,可以象這樣在你的代碼里應用單例模式:
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 context = new VelocityContext(); 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){ //someing invoked in the template //threw an exception }catch(Exception e){} StringWriter writer = new StringWriter(); template.merge(context,writer); |
這是最基本的使用方式,非常簡單!但這恰恰就是當你用Velocity表現一個模版的時候所發生的事情!事實上,你并不一定要嚴格的按照這種方式寫代碼,因為我們為servlet和Application開發人員提供了一些更加簡單的工具。
二、是否使用單例模式
在Velocity1.2和以后的版本中,開發人員有了兩中使用Velocity引擎的方式:單例模式(singleton model)或分離實例模式(separate instance model)。兩種方式采用相同的核心Velocity代碼,這使得更加容易整和Velocity和Java應用程序。
單例模式
在JVM或者web應用中僅僅存在一個Velocity引擎的實例,并且所有應用都共享它。這個實例允許本地化配置和資源共享,可以通過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"); |
org.apache.velocity.servlet.VelocityServlet基類采用了單例模式,這是一個用于幫助開發servlets的實用類。盡管繼承這個類是應用Velocity開發Servlets的最常用、最方便的方法,但是你仍然可以自由選擇是否使用這個類。
分離實例
作為1.2版的一個新特性,你可以在同一個JVM或web應用中創建、配置和使用任意多的Velocity實例。通過org.apache.velocity.app.VelocityEngine類來使用分離的實例。就象下面這樣:
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"); |
正如你所看到的一樣,非常的簡單易懂!除了改變一些簡單的語法,在你的應用中采用單例模式或分離實例模式不需要其他任何改變!
作為程序開發人員,你可以使用org.apache.velocity.app.Velocity和org.apache.velocity.app.VelocityEngine兩個類同Velocity內部交互。但是,請記住,任何時候絕對不要在你的應用程序中使用內部的org.apache.velocity.runtime包中Runtime,RuntimeConstants,RuntimeSingleton或者RuntimeInstance類,因為它們僅僅是供內部使用的,并且以后可能會改變!
三、上下文
基礎
上下文(context)是Velocity的核心概念,也是在系統各部分之間移動“數據容器”的一種常用技術。context作為Java層和模板層的數據載體!作為開發人員,你需要收集你的應用程序所需要的各種類型的對象,并把它們放在context中。作為設計者,可以通過 引用 來存取這些對象。通常,開發人員需要和設計人員一同研究決定應用中需要的數據。因此,這種協同開發值得多花費一些時間并仔細對需求進行分析!
Velocity允許開發人員創建自己的context類來支持特殊需求或技術(LDAP server),并提供了一個基礎的實現類VelocityContext!VelocityContext適用于所有通常的需求,強烈建議使用它!僅僅在特殊和高級的案例中創建自己的context實現。
使用VelocityContext就象使用HashTable一樣簡單,這個接口包含了許多有用的方法,最常用的是:
public Object put(String key,Object value);
public Object get(String key);
需要注意的是,如同HashTable一樣,值必須是Object類型的,并且不能為空!象 int,float等基本數據類型必須包裝成適當的類類型。
這里是一些基本的context操作,更多信息請查看API。
通過#foreach()對迭代對象的支持
作為開發人員,你可以把多種對象存儲到context中,但是也有一些限制,所以應該理解Velocity支持什么類型,以及可能會產生什么問題。Velocity可以在VTL的#foreach()方法中使用很多類型的集合。
·Object[]正常的對象數組。 Velocity會在內部使用一個提供Iterator接口的類來包裝這個數組,但是開發人員和模板設計者不需要關系這個過程。
·java.util.Collection Velocity將調用iterator()方法獲得一個Iterator,所以如果你的類實現了一個Collection接口,請確信iterator()方法會返回一個可用的Iterator.。
·java.util.Map 這里,Velocity將通過values()方法獲得一個Collection接口,并通過這個接口調用iterator()方法獲得Iterator。
·java.util.Iterator 注意:這只是暫時被支持的,原因是Iterator不支持reset。如果一個Iterator被存儲在context中,并且被多個#foreach()方法調用,那么除第一個#foreach()方法外,其他的都將失敗,因為Iterator不支持reset!
·java.util.Enumeration 同上
基于Iterator和Enumeration的限制,強烈建議僅僅在不可避免的時候才使用它們,并且如果可能的話,你應該讓Velocity自己查找適當的可重用的迭代接口。
上下文鏈(Context Chaining)
Velocity的一個創新的特性就是Context Chaining 概念。有時被稱為Context Wrapping,這個高級特性允許你把分離的contexts通過一種方式連接起來,對template而言就象是一個context一樣!
VelocityContext context1 = new VelocityContext(); context1.put("name","Velocity"); context1.put("project", " 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連接起來。這意味著在模板中,你可以存取存放在VelocityContext中的任何對象,只要他們的key不重復就可以,如果key出現重復的話,那么context2中值將是可用的。但值得注意的是,實際上context1中重復的key并沒
有被改變或破壞,仍然可以通過context1.get(“duplicate”)方法獲得context1中deplicate的值!但請記住:在模板中沒有任何方法獲得context1中deplicate的值!同樣,當你通過#set()方法向context中增加信息的時候,這個新的信息將被增加到最外層的context中的key中,所以,請不要試圖將信息通過#set()方法增加到內層的context中!
在Template中創建對象
通常有兩種情況需要在JAVA代碼中處理由template在運行時創建的對象:
·template調用由JAVA代碼放置到context中的對象的方法。
·JAVA代碼在合并后存取由template放置到context中的對象。
關于context的一些其他問題
VelocityContext的一個特性是結點特殊的內省緩存(introspection caching)。通常,作為開發人員可以放心的把VelocityContext做為context使用。但是,必須知道關于這個特性的一個使用模式:
VelocityContext會堆積它訪問過的模板中的語法結點的內省信息。所以在下述情況下:
·重復使用同一個VelocityContext迭代訪問同一個模板
·模板緩存關閉
·在每個反復迭代中調用getTemplate()方法請求獲得Template
VelocityContext可能會導致內存泄漏(實際上是聚集了太多的內省信息)。原因是VelocityContext 會堆積它所訪問過的每個模板的內省信息,如果template緩存被關閉,將導致VelocityContext每次都訪問一個新的模板,從而堆積更多的內省信息。
強烈建議你做如下的事情:
·當template處理結束后創建一個新的VelocityContext,這將阻止堆積內省信息。如果你需要重用攜帶數據和對象的VelocityContext,可以簡單的用另一個VelocityContext來包裝它。外層的VelocityContext將會堆積內省的信息,但另人興奮的是你將丟棄它!
·開啟模板的緩存機制!這將阻止每次都對template重新解析,這樣VelocityContext不僅可以避免增加內省信息,同時還可以改進程序。
·在循環迭代期間重用Template對象。這樣,當緩存關閉的時候就不用強迫Velocity一次又一次的去重新讀取和重新解析同樣的template,因此,VelocityContext也就不會每次都堆積新的內省信息!
四、在Servlets中使用Velocity
Servlet編程
Velocity的一個主要的應用領域就是JAVA Servlet。有很多理由可以說明Velocity適合這個領域,最關鍵的是Velocity強制視圖層(VIEW)和代碼分離!
在servlet中使用velocity非常的簡單。基本上你要做的就是繼承VelocityServlet基類和實現handRequest()方法。
從Velocity1.1開始,有兩個handRequest()方法:
public Template handRequest(Context)
這是舊的方法。這個方法要求返回一個有效的Template對象。如果無效或者為空,將會產生異常!同時error()方法會被調用!如果你希望產生異常后做一些其他的事情(比如重定向)可以重寫這個方法。強烈建議您使用新的方法!
public Template handRequest(HttpServletRequest,HttpServletResponse,Context)
這是新方法。與舊的方法不同,它可以返回一個null來說明方法已經執行,并且Velocity什么都不做。
下面是一個簡單的例子:
public class SampleServlet extends VelocityServlet { public Template handleRequest( HttpServletRequest request, HttpServletResponse response, Context context ) { String p1 = " 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; } } |
是不是覺得很面熟?除了創建context對象,這已經由VelocityServelt幫你做了,并且VelocityServlet也幫你實現了merge()方法,這和我們開始部分的編碼方式基本上是一樣的。我們獲得context和應用程序數據,并反回一個template。
默認傳遞給handleRequest()方法的context對象包含當前的HttpServletRequest和HttpServletResponse對象。他們分別被放置在常量VelocityServlet.REQUEST和VelocityServlet.RESPONSE中。你可以在JAVA代碼中獲得并使用他們:
public Template handleRequest( Context context ) { HttpServletRequest request = (HttpServletRequest) context.get( REQUEST ); HttpServletResponse response = (HttpServletResponse) context.get( RESPONSE ); ... |
也可以在template中使用
#set($name = $req.getParameter('name') ) |
VelocityServelt允許開發者重寫它的一些方法,以更好的使用它。
Properties loadConfiguration(ServletConfig)
這個方法允許重寫通常的配置機制,增加或修改配置屬性。
Context createContext(HttpServletRequest,HttpServletResponse)
這個方法允許你創建自己的Context對象。默認的實現只是簡單的包含request和response的VelocityContext對象。為了避免在一些servlet容器中發生內省緩存問題,這個resquest和response對象被一個簡單的包裹器類包裝了。你可以正常的使用他們,但是請注意他們并不是javax.servlet.XXX !
void mergeTemplage(Template,Context,HttpServletResponse)
這個方法允許你產生輸出流。VelocityServlet提供了一個有效的Writer類的池,所以只有在特殊的情況下才重寫這個方法。
protected void error(HttpServletResquest,HttpServletResponse,Exception)
用于處理在處理請求過程中產生的異常。默認的實現將發送一個包含錯誤信息的簡單HTML給用戶。可以重寫這個方法實現更高級的處理。
部署
當你部署基于Velocity的Servlet時,請確保你的屬性文件被用來配置Velocity運行時刻環境。在Tomcat中,一個不錯的方法就是放置你的velocity.properties文件在你的web應用的根目錄下,并在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> |
Velocity在核心運行時刻類中使用了單例模式,為了確保web應用的classloader會管理你的運行時刻實例,把velocity-xx.jar包放在WEB-INF/lib目錄下是一個好主意!不要放在CLASSPATH或容器的根路徑下。
(待續)
垃圾 講的亂七八糟! 真鄙視你!
狗屎一堆