Velocity官方指南-容器

杜老師說 2022-01-07 08:40:44 阅读数:921

velocity 官方 指南 容器

原文網址  譯者:曾道濤

簡介
“容器”這一概念對於Velocity來說很重要,它是在系統的各部分之間傳遞一系列數據的通用技術。也就是說,容器是Java層(或者程序員)和模板層(或者設計師)之間的數據搬運工。作為程序員,你會收集各種類型的對象,包括所有你程序需要的,然後把它們放在容器裏。對於設計師來說,這些對象,以及它們的方法和屬性,可以通過被稱為引用的模板元素來訪問。一般來說,你會和設計師一起决定應用程序的數據需求。從某種意義上說,一旦你為設計師生成了一個數據集,也即在模板中提供了“API”訪問。因此,在這個階段的開發過程中,你值得花時間仔細分析。

雖然Velocity允許你創建自己的容器類來滿足特殊的需求和技術(比如像一個直接訪問LDAP服務器的容器),一個叫VelocityContext的基本實現類已經作為發行版的一部分提供給你。

VelocityContext適合所有的一般需求,所以我們强烈推薦你使用它。只有在特殊情况和高級應用中,才需要你擴展或者創建你自己的容器實現。

使用VelocityContext就像使用一個普通的Java Hashtable類一樣簡單。雖然接口包含其他有用的方法,我們使用的兩個主要方法是:

public Object put(String key, Object value);public Object get(String key);

請注意,參數value必須繼承自java.lang.Object。基本類型像int和float必須用合適的包裝類包裝。
這就是所有關於容器的基本操作。如需更多信息,可以查看發行版中包含的API文檔。

 

for和foreach()遍曆對象的支持

作為一個程序員,對於你放在容器中的對象有很大的自主權。但是正如大多數自主權,這也需要一點點責任,所以要理解Velocity支持什麼,以及任何可能出現的問題。Velocity支持幾種集合類型在VTL中使用foreach()語句。
•  Object []   普通對象數組,在這裏無需多說。如果一個類中提供了迭代器接口,Velocity會自動包裝你的數組,但是這不應該涉及到程序員和模板的設計者。更有趣的是,Velocity現在允許模板設計者把數組當作定長鏈錶來處理(Velocity 1.6中就是這樣)。這意味著他們既可以在數組上也可以在java.util.List的實例上調用像size(), isEmpty()和get(int)這樣的方法,而無需關心它們本身的差异。
•  java.util.Collection  Velocity通過iterator()方法返回一個迭代器在循環中使用,所以如果你正在你的對象上實現一個集合接口,請確保iterator()方法返回一個有效的迭代器。
•  java.util.Map    這裏,Velocity通過接口的values()方法返回一個Collection接口,iterator()方法在它上面調用來檢索用於循環的迭代器。
•  java.util.Iterator   使用的時候需要注意:目前只是暫時支持,關注的問題是迭代器不能重置。如果一個未初始化的迭代器被放進了容器,並且在多個foreach()語句中使用,如果第一個foreach()失敗了,後面的都會被阻塞,因為迭代器不會重啟。
•  java.util.Enumeration     使用的時候需要注意:和java.util.Iterator一樣,目前只是暫時支持,關注的問題是枚舉不能重置。如果一個未初始化的枚舉被放進了容器,並且在多個foreach()語句中使用,如果第一個foreach()失敗了,後面的都會被阻塞,因為枚舉不會重啟。

•  任何擁有public Iterator iterator()方法的公有類永遠不會返回null。Velocity會把尋找一個iterator()方法作為最後的手段。這提供了很大的靈活性,也自動支持Java 1.5中的java.util.Iterable接口。

對於Iterator和Enumeration,推薦只有在萬不得已的情况下才把它們放進容器,你也應該盡可能地讓Velocity找到合適的、可複用的迭代接口。

雖然有充足的理由直接使用java.util.Iterator接口(比如像JDBC這樣的大數據集),但是如果能够避免,用其他的可能會更好。“直接”是說像下面這樣:

Vector v = new Vector();v.addElement("Hello");v.addElement("There");context.put("words", v.iterator() );

當迭代器本身被放進了容器。當然,你也可以簡單地這樣做:

context.put("words", v );

兩種方式都可以:Velocity能够識別實現了Collection(像List)接口的Vector,並因此找到iterator()方法,在它每次需要的時候調用iterator()來刷新迭代器。一旦Velocity在foreach()中使用了一個空迭代器(先跳過這裏…),Velocity沒辦法為它正在使用的下一個foreach()獲取一個新的迭代器。結果是後面任何使用空迭代器引用的foreach()都會阻塞,並且沒有輸出。

上面這些並不是為了錶明在Velocity中遍曆集合需要十分小心。恰恰相反,一般來說,只要在你向容器中添加迭代器時小心就可以了。

對靜態類的支持
並非所有的類都可以實例化。像java.lang.Math這樣的類不提供任何公有的構造函數,但是它包含了有用的靜態方法。為了從模板中訪問這些靜態方法,你可以簡單地把這些類自身添加到容器中:

context.put("Math", Math.class);

這樣你就可以在模板中用$Math引用調用java.lang.Math中的任何公有靜態方法。

容器鏈

Velocity容器設計的一大創新特性是容器鏈的概念。有時也被稱為contextwrapping,這個高級特性允許你以一種方式把獨立的容器連接在一起,使它們對模板來說像一個連續的容器。

最好用一個例子來說明:

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。這意味著在模板中,你可以訪問這兩個VelocityContext對象中放置的任意項,只要在添加對象的時候沒有重複的鍵。如果遇到這種情况,就像上面的鍵”duplicate”,最近的容器中存儲的對象可以被訪問。在上面這個例子中,返回的對象是字符串”I am in context2″。

注意,這種容器中對象的重複或者“覆蓋”,不會以任何方式損壞或者修改被覆蓋的對象。所以在上面的例子中, 字符串”I am in context1″還存在並且完好無損,仍然可以通過context1.get(“duplicate”)訪問。但是在上面的例子中,模板中引用”$duplicate”的值會是”I am in context2″,模板不能訪問被覆蓋的字符串”I am in context1″。

也要注意,當你使用模板向一個渲染之後再檢查的容器中添加信息的時候,你必須要小心。在一個模板中,通過set()語句改變容器只會影響外層的容器。所以在期望模板中的數據已經被添加到內部容器時,請確保你沒有丟弃外層的容器。

這個特性有很多用途,目前最常用的就是提供層次數據訪問和工具箱。

就像前面提到過的,Velocity容器機制是可以擴展的,但是超出了本指南的當前範圍。如果你有興趣,可以查看包org.apache.velocity.context中的類,看看提供的容器怎麼組合在一起。更進一步,有幾個例子在發行版的examples/context_example目錄下,它們展示了可能的實現,包括一個使用數據庫作為後臺存儲的例子。

請注意,這些例子不被支持,它們僅僅是出於示範和教學目的。

模板中創建的對象
在模板中,有兩種情况Java代碼必須處理運行時創建的對象:

當模板設計者通過Java代碼調用容器中放置的對象的方法:
#set($myarr = [“a”,”b”,”c”] )
$foo.bar( $myarr )

當模板向容器中添加對象,Java代碼可以在合並過程完成後訪問這些對象:
#set($myarr = [“a”,”b”,”c”] )
#set( $foo = 1 )
#set( $bar = “bar”)

如果非常直接地處理這些情况,有幾個事情需要知道:
•  當放在容器中或者傳給方法時,VTL RangeOperator [ 1..10 ]和ObjectArray [“a”,”b”]是java.util.ArrayList對象。所以,當你設計接受模板中創建的數組的方法時,你應該牢記這個問題。
•  VTL Map引用毫無疑問是用java.util.Map存儲的。
•  小數在容器中會是Doubles或者BigDecimals,整數會是 Integer, Long,或者BigIntegers字符串當然還是Strings。
•  Velocity在調用方法時會適當地省略參數,所以調用setFoo( int i )把一個整數放入容器,#set()和它等價。

其他容器問題
VelocityContext(或者任何派生自AbstractContext的Context)提供的一個特性是節點特有的自我監視緩存。一般來說,作為一個開發者,當你使用VelocityContext作為你的容器時,不必擔心這些。但是,有一個目前已知的使用模式,你必須知道這個特性。

當VelocityContex訪問模板中的節點時,它會收集關於這些有序節點的自我監視信息。所以,在下面這幾種情况:
•  你使用同一個VelocityContext對象遍曆同樣的模板。
•  模板緩存關閉。
•  在每次迭代中,你通過getTemplate()請求模板。

你的VelocityContext有可能出現內存泄露(在收集更多自我監視信息時真會出現)。真正發生的是,它為每個它訪問的模板收集模板節點的自我監視信息,當模板緩存關閉時,對VelocityContext來說它每次都是訪問一個新模板。因此,它收集更多的自我監視信息,並且一直增長。强烈推薦你做以下一條或者更多條:

•  通過模板補償處理,每次遍曆都新建一個VelocityContext。這樣就不會收集自我監視緩存數據。在你由於VelocityContext已經填充了數據或對象而想重用它的情况下,你可以簡單地把填充好的VelocityContext包裝成另一個VelocityContext,外層的那個會收集自我監視信息,你直接丟弃就可以了。例如 VelocityContext useThis = new VelocityContext( populatedVC );它可以很好地工作,因為外層的容器會存儲自我監視緩存數據,它可以從內部容器獲取任何請求的數據(當它為空時)。你仍然要注意,如果你的模板把數據放入容器並且期待後面的遍曆使用這些數據,你需要做另外一個准備,因為任何模板set()語句會被存儲在最外層的容器。看“Context chaining”章節中的討論來獲取更多信息。
•  打開模板緩存。這樣可以避免在每次遍曆中重複解析模板,因此VelocityContext不僅可以避免增加自我監視的緩存信息,還可以帶來性能的提昇。
•  在循環遍曆的過程中重用模板對象。如果緩存關閉了,你不必讓Velocity一遍又一遍地重複讀取和解析相同的模板,所以VelocityContext就不會每次都收集新的自我監視信息。

原創文章,轉載請注明: 轉載自並發編程網 – ifeve.com本文鏈接地址: Velocity官方指南-容器

FavoriteLoading添加本文到我的收藏
版权声明:本文为[杜老師說]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201070840439858.html