FreeMarker開發-數據模型

  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
相關文章
相關標籤/搜索