FreeMarker用於處理模板的數據模型是哈希表,也就是一個樹狀結構的name-value對。以下:java
(root)
|
+- string="string"
|
+- map
| |
| +- map1 = "map1"
| |
| +- map2 = "map2"
|
+- object
| |
| +- field1= "field1"
| |
| +- field2 = "field1"數組
| |緩存
| +- method= "method"
|
......app
在Java代碼中(下面第15行),咱們提供給模板引擎的數據(process方法的第一個參數),能夠是Map,也能夠是自定義的Java對象。可是,模板引擎在處理時,並非直接使用咱們提供的類型。它會將其轉換爲本身內部定義的類型,轉換工做由第8行的ObjectWrapper去完成,這種特性被稱做「對象包裝(Object Swapping)」。ide
查看源碼,關於Template#process(Object dataModel, Writer out)方法中dataModel參數的解釋以下:工具
dataModel是從模板中能夠獲取的變量的容器(name-value對);oop
dataModel一般是一個Map<String, Object>或者是一個JavaBean(JavaBean的屬性將成爲變量);ui
能夠是BeanWrapper在執行時能夠轉換爲TemplateHashModel的任意對象;spa
也能夠是實現了TemplateHashModel接口的對象,這種狀況在執行時將直接使用,再也不進行包裝;code
若是是null,則使用一個空的數據模型。
1 public class FreeMarkerTest { 2 public static void main(String[] args) throws IOException, 3 TemplateException { 4 Configuration conf = new Configuration(); 5 // 設置加載模板文件的目錄
6 conf.setDirectoryForTemplateLoading(new File("src/templates")); 7 // 設置模板檢索數據模型的方式
8 conf.setObjectWrapper(new DefaultObjectWrapper()); 9 // 建立、解析模板並緩存
10 Template template = conf.getTemplate("example.flt"); 11 // 準備數據
12 Map<String, Object> root = new HashMap<String, Object>(); 13 root.put("example", "Hello World!"); 14 // 將數據與模板合併
15 template.process(root, new OutputStreamWriter(System.out)); 16 } 17 }
1、對象包裝(Object Swapping)
FreeMarker數據模型的哈希表中的name爲字符串,value能夠爲標量、容器、子程序和節點等。這也是FreeMarker內部使用的變量的類型。這些類型都實現了freemarker.template.TemplateModel接口。
1. 標量:包括數值、字符串、日期、布爾。
2. 容器:包括哈希表(Map,相似於Java中的Map)、序列(Sequence,相似於Java中的數組和有序集合)、集合(Collection,相似於Java中的集合)。
3. 子程序:包括方法(Method)和指令(Directive)。
4. 節點:主要是爲了幫助用戶在數據模型中處理XML文檔。
在模板處理時,會將Java類型包裝爲對應的TemplateModel實現。好比將一個String包裝爲SimpleScalar來存儲一樣的值。對於每一個Java類型,具體選擇什麼TemplateModel實現去包裝,取決於對象包裝器(ObjectWrapper)的實現策略,可經過上面代碼第8行設置。ObjectWrapper是一個接口,FreeMarker核心包提供了兩個基本的實現:ObjectWrapper.DEFAULT_WRAPPER、ObjectWrapper.BEANS_WRAPPER。
1. ObjectWrapper.DEFAULT_WRAPPER: 按照下表對應關係包裝,Jython類型包裝爲freemarker.ext.jython.JythonWrapper,其它類型調用BEANS_WRAPPER。
2. ObjectWrapper.BEANS_WRAPPER:利用Java反射來獲取對象的成員屬性。
類型 | FreeMarker接口 | FreeMarker實現 |
字符串 | TemplateScalarModel | SimpleScalar |
數值 | TemplateNumberModel | SimpleNumber |
日期 | TemplateDateModel | SimpleDate |
布爾 | TemplateBooleanModel | TemplateBooleanModel.TRUE |
哈希 | TemplateHashModel | SimpleHash |
序列 | TemplateSequenceModel | SimpleSequence |
集合 | TemplateCollectionModel | SimpleCollection |
節點 | TemplateNodeModel | NodeModel |
ObjectWrapper的這兩個屬性現已經被標註爲@deprecated,並建議用DefaultObjectWrapperBuilder#build()和BeansWrapperBuilder#build()方式獲取實例。以下:
// conf.setObjectWrapper(new DefaultObjectWrapperBuilder(new Version("2.3.22")).build());
conf.setObjectWrapper(new BeansWrapperBuilder(new Version("2.3.22")).build());
2、自定義方法
自定義方法須要實現freemarker.template.TemplateMethodModel接口(當前已@deprecated),建議替換爲TemplateMethodModelEx。
例:實現累加方法sum(int...num)。參數能夠有多個整數。
模板以下:
${sum(1, 2, 3, 4)}
代碼以下:
package org.genein.freemark.templateModel; import java.util.List; import freemarker.template.SimpleNumber; import freemarker.template.TemplateMethodModelEx; import freemarker.template.TemplateModelException; public class SumMethod implements TemplateMethodModelEx { @SuppressWarnings("rawtypes") @Override public Object exec(List arg0) throws TemplateModelException { if (arg0 == null || arg0.size() == 0) { return new SimpleNumber(0); } double sum = 0l; double tmp; for (int i = 0; i < arg0.size(); i++) { tmp = Double.valueOf(arg0.get(i).toString()); sum += tmp; } return new SimpleNumber(sum); } }
FreeMarkerTest.main方法添加如下代碼:
1 // 添加方法工具
2 root.put("sum", new SumMethod()); 3 // 將數據與模板合併
4 template.process(root, new OutputStreamWriter(System.out));
輸出以下:
10
三、自定義指令
自定義指令須要實現freemarker.template.TemplateDirectiveModel接口。
例:自定義一個指令,循環輸出內嵌內容,count參數決定循環次數(必填),hr參數決定是否添加分隔符「<hr>」(可選,默認false),step參數表示當前循環次數(可選)。
模板以下:
<@repeat count=5 hr=false; step> ${step}. ${name} </@repeat>
代碼以下:
package org.genein.freemark.templateModel; import java.io.IOException; import java.util.Map; import freemarker.core.Environment; import freemarker.template.SimpleNumber; import freemarker.template.TemplateBooleanModel; import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateDirectiveModel; import freemarker.template.TemplateException; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; import freemarker.template.TemplateNumberModel; public class RepeatDirective implements TemplateDirectiveModel { /** * 循環次數 */
private static final String COUNT = "count"; /** * 是否須要用hr標籤間隔 */
private static final String HR = "hr"; @SuppressWarnings("rawtypes") @Override public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { // 獲取count參數,並校驗是否合法
TemplateModel countModel = (TemplateModel) params.get(COUNT); if (countModel == null) { throw new TemplateModelException("缺乏必須參數count!"); } if (!(countModel instanceof TemplateNumberModel)) { throw new TemplateModelException("count參數必須爲數值型!"); } int count = ((TemplateNumberModel) countModel).getAsNumber().intValue(); if (count < 0) { throw new TemplateModelException("count參數值必須爲正整數!"); } // 獲取hr參數,並校驗是否合法
boolean hr = false; TemplateModel hrModel = (TemplateModel) params.get(HR); if (hrModel != null) { if (!(hrModel instanceof TemplateBooleanModel)) { throw new TemplateModelException("hr參數值必須爲布爾型!"); } hr = ((TemplateBooleanModel) hrModel).getAsBoolean(); } // 檢驗內嵌內容是否爲空
if (body == null) { throw new RuntimeException("內嵌內容不能爲空!"); } // 最多隻容許一個循環變量
if (loopVars.length > 1) { throw new TemplateModelException("最多隻容許一個循環變量!"); } // 循環渲染內嵌內容
for (int i = 0; i < count; i++) { // 用第一個循環變量記錄循環次數
if (loopVars.length == 1) { loopVars[0] = new SimpleNumber(i + 1); } // 上面設置循環變量的操做必須在該render前面,由於內嵌內容中使用到了該循環變量。 // body.render(new UpperCaseFilterWriter(env.getOut()));
body.render(env.getOut()); if (hr) { env.getOut().write("<hr>"); } } } }
FreeMarkerTest.main增長如下代碼:
root.put("name", "Genein"); // 添加自定義指令
root.put("repeat", new RepeatDirective()); // 將數據與模板合併
template.process(root, new OutputStreamWriter(System.out));
輸出以下:
1. Genein
2. Genein
3. Genein
4. Genein
5. Genein