阅读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页面显示新闻.公告之类的上一篇下一篇