閱讀263 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Freemarker 高級進階

博學,切問,近思--詹子知 (https://jameszhan.github.io)


 這篇文章我們將不介紹Freemarker的基本語法,先來個工具模板utils.ftl,因為下麵的操作中會用到這個文件。

<#macro mapping map> <#list map?keys as key> ${key}: ${(map[key])!} </#list> </#macro> <#macro listing list> <#list list as item> ${item} </#list> </#macro> <#macro repeat count> <#list 1..count as i>${i}<#nested></#list> </#macro>

 

1. Model 的注冊

 

Map<String, Object> rootMap = new HashMap<String, Object>(); rootMap.put("hello", "James"); rootMap.put("env", System.getenv()); rootMap.put("this", rootMap); Configuration cfg = new Configuration(); Template template = cfg.getTemplate("model.ftl", locale); template.process(rootMap, out); //model.ftl content is <#import "utils.ftl" as util /> Hello ${hello} this hello ${this.hello} <@util.mapping env />

Output is:

Hello James this hello James USERPROFILE: C:/Documents and Settings/011447.SZ PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH USERDNSDOMAIN: SZ.VSG.COM __COMPAT_LAYER: EnableNXShowUI JAVA_HOME: D:/toolkit/sdk/jdk1.6.0_14 CURL_HOME: D:/toolkit/sdk/curl-7.19.5 MINGW_HOME: D:/toolkit/sdk/mingw SystemDrive: C: TEMP: C:/DOCUME~1/011447.SZ/LOCALS~1/Temp MYSQL_HOME: D:/toolkit/sdk/mysql-5.1.35-win32 ProgramFiles: C:/Program Files Path: D:/toolkit/sdk/jdk1.6.0_14/bin/../jre/bin/client;D:/toolkit/sdk/jdk1.6.0_14/bin/../jre/bin;D:/toolkit/sdk/jdk1.6.0_14/bin/../jre/lib/i386;D:/msys/bin;D:/toolkit/sdk/ParserGenerator2/Bin;D:/toolkit/sdk/mingw/bin;D:/toolkit/sdk/curl-7.19.5/;C:/Python31/;D:/toolkit/sdk/apache-tomcat-6.0.20/bin;D:/toolkit/sdk/jdk1.6.0_14/bin;D:/toolkit/sdk/CLisp;D:/toolkit/sdk/mysql-5.1.35-win32/bin;D:/toolkit/sdk/javafx-sdk1.2/bin;D:/toolkit/sdk/javafx-sdk1.2/emulator/bin;C:/WINDOWS/system32;C:/WINDOWS;C:/WINDOWS/System32/Wbem;C:/Program Files/QuickTime/QTSystem/;C:/Program Files/StormII/Codec/QTSystem/ HOMEDRIVE: C: PROCESSOR_REVISION: 170a CLIENTNAME: Console USERDOMAIN: SZ QTJAVA: D:/toolkit/sdk/jre6/lib/ext/QTJava.zip ALLUSERSPROFILE: C:/Documents and Settings/All Users.WINDOWS PROCESSOR_IDENTIFIER: x86 Family 6 Model 23 Stepping 10, GenuineIntel SESSIONNAME: Console YACC_HOME: D:/toolkit/sdk/ParserGenerator2 AVR32_HOME: D:/toolkit/WinAVR-20100110 TMP: C:/DOCUME~1/011447.SZ/LOCALS~1/Temp PYTHON_HOME: C:/Python31 PROCESSOR_ARCHITECTURE: x86 LOGONSERVER: //SZ-DC-01 =::: ::/ CLASSPATH: .;D:/toolkit/sdk/jre6/lib/ext/QTJava.zip CommonProgramFiles: C:/Program Files/Common Files OS: Windows_NT FP_NO_HOST_CHECK: NO HOMEPATH: /Documents and Settings/011447.SZ PROCESSOR_LEVEL: 6 CATALINA_HOME: D:/toolkit/sdk/apache-tomcat-6.0.20 LISP_HOME: D:/toolkit/sdk/CLisp COMPUTERNAME: SZ-EXP-011447 SystemRoot: C:/WINDOWS windir: C:/WINDOWS NUMBER_OF_PROCESSORS: 2 USERNAME: 011447 TOMCAT_HOME: D:/toolkit/sdk/apache-tomcat-6.0.20 ComSpec: C:/WINDOWS/system32/cmd.exe MSYS_HOME: D:/msys APPDATA: d:/user/011447/Application Data

在做這個事情之前,我們先準備一些工具方法。

//拿到靜態Class的Model public TemplateModel useClass(String className) throws TemplateModelException { BeansWrapper wrapper = (BeansWrapper) cfg.getObjectWrapper(); TemplateHashModel staticModels = wrapper.getStaticModels(); return staticModels.get(className); } //拿到目標對象的model public TemplateModel useObjectModel(Object target) throws TemplateModelException { ObjectWrapper wrapper = cfg.getObjectWrapper(); TemplateModel model = wrapper.wrap(target); return model; } //拿到目標對象某個方法的Model public TemplateModel useObjectMethod(Object target, String methodName) throws TemplateModelException { TemplateHashModel model = (TemplateHashModel) useObjectModel(target); return model.get(methodName); }

 2.靜態類注冊,有的時候,我們經常需要在freemarker頁麵中使用java中的一些靜態類的方法和屬性,下麵就演示這種情形的注冊方式。

Map<String, Object> rootMap = new HashMap<String, Object>(); rootMap.put("static", ((BeansWrapper)cfg.getObjectWrapper()).getStaticModels()); rootMap.put("thread", useClass("java.lang.Thread")); rootMap.put("system", useClass("java.lang.System")); //利用Freemarker在控製台輸出一條消息: "Hello World" ${system.out.println("Hello World!")} //格式化字符串輸出: ${static["java.lang.String"].format("%-20s%-20s%-20s", "", thread.currentThread().getName(), thread.activeCount())}

 

3.對象注冊

rootMap.put("currentTime", Calendar.getInstance()); //如果不想暴露整個對象的信息,就可以使用單個方法的注冊形式。 rootMap.put("dateformat", useObjectMethod(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss S"), "format")); //Sample: Current time is ${dateformat(currentTime.time)} TimeZone: ${currentTime.timeZone.ID}-${currentTime.timeZone.displayName} Output: Current time is 2010-08-17 02:06:11 48 TimeZone: Asia/Shanghai-中國標準時間

 

4.注冊共享對象,有的時候我們可能希望注冊的對象能夠被所有的freemarker文件訪問得到,那麼可以使用configuration.setSharedVariable(String name, TemplateModel tm) 方法注冊Model。

 

5.Dot擴展

很多時候,我們可能希望為我們的model定義一個有層次結構意義的名字,比如"system.admin", "user.id" etc... 由於.在Freemarker的解析中有特殊的含義,所以如果我們注冊的Model注冊的名字中如果含有.的話,你就無法在頁麵上拿到正確的對象信息。下麵這個類提供了Dot擴展的功能,注意registerGlobalModel方法。

 

package com.apple.ext.freemarker; import java.io.IOException; import java.io.StringWriter; import java.util.Locale; import freemarker.cache.ClassTemplateLoader; import freemarker.ext.beans.BeansWrapper; import freemarker.ext.beans.MethodCheckingObjectWrapper; import freemarker.template.Configuration; import freemarker.template.ObjectWrapper; import freemarker.template.Template; import freemarker.template.TemplateException; import freemarker.template.TemplateHashModel; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; public class ModelRegistrar { private Configuration cfg; public ModelRegistrar() { cfg = new Configuration(); cfg.setTemplateLoader(new ClassTemplateLoader(this.getClass(), "")); cfg.setObjectWrapper(new MethodCheckingObjectWrapper()); } public ModelRegistrar(Configuration cfg) { this.cfg = cfg; } public Configuration getConfiguration() { return cfg; } public void registerGlobalMethod(String key, Object target, String methodName) throws TemplateModelException { registerGlobalModel(key, useObjectMethod(target, methodName)); } public void registerGlobalModel(String key, Object target) throws TemplateModelException { String[] keys = key.split("//."); if (keys.length < 1) { throw new IllegalArgumentException("Invalid model registration key: " + key); } TemplateModel model = cfg.getObjectWrapper().wrap(target); TemplateModel exist = cfg.getSharedVariable(keys[0]); if (exist == null) { cfg.setSharedVariable(keys[0], buildDeepTemplateMap(keys, 1, model)); } else { MapDotModel registry = null; int i = 0; for (; i < keys.length - 1; i++) { if (exist == null) { registry.put(keys[i], buildDeepTemplateMap(keys, i + 1, model)); return; } if (exist instanceof MapDotModel) { registry = (MapDotModel) exist; exist = registry.get(keys[i + 1]); } else { throw new IllegalArgumentException("model '" + key + "' at '" + keys[i] + "' already defined and can't be added because the existing type is '" + exist.getClass().getName()); } } if (exist != null) { throw new IllegalArgumentException("model '" + key + "' at '" + keys[i] + "' has conflicting updates for '" + exist.toString() + "' and '" + target.toString() + "'"); } registry.put(keys[i], buildDeepTemplateMap(keys, i + 1, model)); } } private TemplateModel buildDeepTemplateMap(String[] keys, int start, TemplateModel target) { for(int i = keys.length - 1; i >= start; i--) { MapDotModel model = new MapDotModel(); model.put(keys[i], target); target = model; } return target; } public TemplateModel useObjectModel(Object target) throws TemplateModelException { ObjectWrapper wrapper = cfg.getObjectWrapper(); TemplateModel model = wrapper.wrap(target); return model; } public TemplateModel useObjectMethod(Object target, String methodName) throws TemplateModelException { TemplateHashModel model = (TemplateHashModel) useObjectModel(target); return model.get(methodName); } public TemplateModel useClass(String className) throws TemplateModelException { BeansWrapper wrapper = (BeansWrapper) cfg.getObjectWrapper(); TemplateHashModel staticModels = wrapper.getStaticModels(); return staticModels.get(className); } }

然後我們就可以使用它提供的功能去做我們的擴展了:

ModelRegistrar mr = new ModelRegistrar(); mr.registerGlobalModel("global.string", mr.useClass("java.lang.String")); SimpleDateFormat sdf = new SimpleDateFormat("'Current time is' yyyy-MM-dd hh:mm:ss S"); mr.registerGlobalMethod("global.date.format", sdf, "format"); mr.registerGlobalModel("global.date.currentTime", new Date()); //Freemarker: ${global.string.format("This is the string format string, see the grap. %60s", "arguments")} ${global.date.format(global.date.currentTime)} //output sample: This is the string format string, see the grap. arguments Current time is 2010-08-17 02:19:44 856

 

最後,還需要提到一些在擴展方麵需要注意的地方,並不是對象所有的方法和屬性都可以被Freemarker訪問得到的,對於static方法和屬性來說,如果類注冊到model中後,我們可以使用它的名字去調用或者訪問它。而對於成員變量來說,如果需要訪問它,則必須為它指定一個get方法。另外,有些方法是被標注為unsafe的,例如java.lang.System.exit(), java.lang.Object.wait(), 詳細情況參考freemarker.ext.beans.unsafeMethods.txt 和 freemarker.ext.beans.BeansWrapper.

最後更新:2017-04-02 06:51:23

  上一篇:go windows常用快捷鍵,大大提高效率
  下一篇:go jsp頁麵顯示新聞.公告之類的上一篇下一篇