自己模拟的ftl 用法:
基类 public class Ftl_object_data_model { //三种基本属性 private boolean canRead=true;//是否能读取 private int length=2;//长度 private String content;//内容 //内部小vo对象属性 private Ftl_object_data_model_inner fi = new Ftl_object_data_model_inner(); //非属性内部方法1 public boolean isCanWrite() { return true; } //非属性内部方法2 public boolean canSend() { return true; } public boolean isCanRead() { return canRead; } public void setCanRead(boolean canRead) { this.canRead = canRead; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Ftl_object_data_model_inner getFi() { return fi; } public void setFi(Ftl_object_data_model_inner fi) { this.fi = fi; } // @Override // public Object exec(List arg0) throws TemplateModelException { // return new SimpleNumber(((SimpleScalar)arg0.get(1)).getAsString().indexOf(((SimpleScalar)arg0.get(0)).getAsString())); // } // public TemplateModel exec(List arg0) throws TemplateModelException { // return new SimpleNumber(((String)arg0.get(1)).indexOf((String)arg0.get(0))); // } } 基类中小vo 的类 public class Ftl_object_data_model_inner { private boolean canRead=true;//是否能读取 private int length;//长度 private String content;//内容 public boolean isCanRead() { return canRead; } public void setCanRead(boolean canRead) { this.canRead = canRead; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } main启动测试类 public class FtlTest { public static void main(String[] args) throws IOException { Configuration cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(new File("F:\\king\\proj\\TKCard\\TKCard_cn_cn_Server\\src\\test\\java\\general\\ftl_test")); // cfg.setObjectWrapper(ObjectWrapper.DEFAULT_WRAPPER); cfg.setObjectWrapper(new DefaultObjectWrapper()); cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); //生成数据模型 //用map封装的数据模型 // Object root = testMapDataModel(); //直接用对象封装的数据模型 Object root = testObjectDataModel(); // 通过freemarker解释模板,首先需要获得Template对象 //用map封装的数据模型对应的模板 // Template template = cfg.getTemplate(testMapDataModelTemplate()); //直接用对象封装的数据模型对应的模板 Template template = cfg.getTemplate(testObjectDataModelTemplate()); // 定义模板解释完成之后的输出 //PrintWriter out = new PrintWriter(new BufferedWriter( new FileWriter(dir+"/out.txt"))); //直接控制台打印 OutputStreamWriter out = new OutputStreamWriter(System.out); try { // 解释模板 template.process(root, out); } catch (TemplateException e) { e.printStackTrace(); } } /** * 测试用map封装的数据模型 * @return */ public static Object testMapDataModel(){ Map root = new HashMap(); root.put("name1", "世界,你好");//模板里需要几个值,这边数据模型里就必须要塞几个值,缺一个就会报错(多塞不会报错),因为freeworker 不支持null,所以少塞会报错 root.put("name2", "我is大开发建设领导看风景"); root.put("name3", new Ftl_object_data_model());//如果传的是方法则上边map类型的数据模型的map只能去掉泛型了,不能用key为value皆为string的泛型了 return root; } /** * 测试用map封装的数据模型对应的数据模板 * @return */ public static String testMapDataModelTemplate(){ return "ftl_map_data_model.ftl"; } /** * 测试用vo对象封装的数据模型 * @return */ public static Object testObjectDataModel() { Ftl_object_data_model fm = new Ftl_object_data_model(); return fm; } /** * 测试用vo对象封装的数据模型对应的数据模板 * @return */ public static String testObjectDataModelTemplate(){ return "ftl_object_data_model.ftl"; } } 数据模板 ---------------------------------------- 一、------------------------------------- <#-- 一、整个vo对象直接作为数据模型数据的基类Ftl_object_data_model 对象中的属性拿取方式--> <#--基础属性直接拿取,无需带上对象变量(由于这边无需变量名,所以此基类变量名既不是root又不是fm)--> <#--${root.length} 错误的写法,无需带root--> <#--${fm.length} 错误的写法,无需带fm--> <#--基类属性读取直接拿即可,其他属性一样,包括方法,直接拿取--> ${length} <#--由于ftl一般都以字符串形式打印,所以Boolean类型的读取要特殊处理--> <#--${canRead} 内部Boolean类型属性错误读取--> <#--${canread} 内部Boolean类型属性错误读取--> <#--${isCanRead()} 内部Boolean类型属性错误读取--> <#--Boolean类型正确读写 固定格式 var?c --> <#--boolean第一种读取,结果打印true or false--> ${canRead?c} <#--Boolean第二种读取,结果打印yes or no 当canRead为真则取第一个参数值即yes,否则取第二个参数no--> ${canRead?string('yes','no')} <#--非属性 的 Boolean类型get方法读取,即这个get方法拿的不是对象的属性,而仅仅是类中一个返回值为Boolean类型的方法--> 方法1:<#--${CanWrite()?c} 拿对象中非属性内部方法1错误拿法--> 方法1:<#--${canWrite()?c} 拿对象中非属性内部方法1错误拿法--> 方法1:<#--${canwrite()?c} 拿对象中非属性内部方法1错误拿法--> 方法1:<#--<#if CanWrite >true</#if> 拿对象中非属性内部方法1错误拿法--> 方法1:<#--<#if canwrite >true</#if> 拿对象中非属性内部方法1错误拿法--> 方法1:${isCanWrite()?c} <#-- 拿对象中非属性内部方法1正确拿法1--> 方法1:<#if isCanWrite() >true</#if> <#--拿对象中非属性内部方法1正确拿法--> 方法1:<#if canWrite >true</#if> <#--拿对象中非属性内部方法1正确拿法,这种方法是一种特殊的拿取方式,必须是在严格的is打头的get方法时才有用--> 方法2:<#--${cansend()?c} 拿对象中非属性内部方法2错误拿法--> 方法2:<#--<#if canSend >true</#if> 拿对象中非属性内部方法2错误拿法--> 方法2:<#--<#if cansend >true</#if> 拿对象中非属性内部方法2错误拿法--> 方法2:${canSend()?c} <#-- 拿对象中非属性内部方法2正确拿法1--> 方法2:<#if canSend() >true</#if> <#--拿对象中非属性内部方法2正确拿法--> ---------------------------------------- 二、------------------------------------- <#-- 二、整个vo对象直接作为数据模型数据的基类Ftl_object_data_model 对象中的子类属性 小vo对象中的属性拿取方式--> <#--直接用小vo 被new 时的变量名.属性 或 .方法即可 拿取 方式和直接拿基类中的属性一样的--> ${fi.length} ${fi.isCanRead()?c} <#if fi.isCanRead() >true</#if> <#if fi.canRead >true</#if> <#-- 注:几个重要的语法规则: 1、FreeMarker的插值有如下两种类型:1,通用插值${expr};2,数字格式化插值:#{expr}或#{expr;format} 2、??:判断某个变量是否存在,返回boolean值 类似 var?if_exists 3、var!xxx:指定缺失变量的默认值 类似 var?default(xxx) 而 ${!var?c}表明非var,即当var为真时结果求假,!此表非含义 4、使用lt、lte、gt和gte来替代<、<=、>和>= 也可以使用括号<#if (x>y)> 5、内置函数: 调用区别于属性的访问,属性访问用.访问,而内置函数用?访问如var?if_exists 6、注意${}为变量的渲染显示,而<>为定义等操作符的定义 7、对于库中的变量修改,使用in关键字 <#assign mail="jsmith@other.com" in my> 8、stringA[M .. N] 取子字符串,类似substring(stringA, M, N) 9、List片段可以采用: products[10..19] or products[5..] 的格式进行定义,当只局限于数字 <#assign c= [1,2,3,4,5,6,6,7]> <#list c[1..3] as v> ${v} </#list> 10、freemarker可用"["代替"<".在模板的文件开头加上[#ftl]. 11、注释语法 <#-- 注释部分 --> <#-- 由于杀念11注释语法破坏了整体的注释,所以这边由加了个注释头标签 12、 数字输出的另外一种方式 #{c.a;m0} 区别于${x},这个例子是用于输出数字的格式化,保留小数的位数,详细如下 数字格式化插值可采用#{expr;m1M2}形式来格式化数字,其中: m1:小数部分最小1位 M2:小数部分最大2位 即大小m代表不同含义 13、在定义字符串的时候,可以使用''或者"",对特殊字符,需要使用\进行转义 14、如果存在大量特殊字符,可以使用${r"..."}进行过滤 如 ${r"${foo}"} ${r"C:\foo\bar"} 15、Map对象的key和value都是表达式,但是key必须是字符串 可以混合使用.和[""]访问 book.author["name"] //混合使用点语法和方括号语法 16、为了处理缺失变量,FreeMarker提供了两个运算符: 用于防止对象不存在而导致的异常 !:指定缺失变量的默认值 ??:判断某个变量是否存在,返回boolean值 17、noparse指令指定FreeMarker不处理该指定里包含的内容,该指令的语法格式如下: <#noparse>...</#noparse> 18、?html 用于将字符串中可能包含的html字符,进行过滤. ${firstName?html} 使用?html对html字符进行过滤、格式化处理 escape , noescape指令,对body内的内容实用统一的表达式 看如下的代码: <#escape x as x?html> First name:${firstName} Last name:${lastName} Maiden name:${maidenName} </#escape> 上面的代码等同于: First name:${firstName?html} Last name:${lastName?html} Maiden name:${maidenName?html} 19、<#assign name1=value1 name2=value2 / > 可以同时定义多个变量 也可以使用循环来给变量赋值 <#assign x> <#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as n> n:${n} </#list> </#assign> x:${x} 执行结果: x: n:星期一 n:星期二 n:星期三 n:星期四 n:星期五 n:星期六 n:星期天 说明:不是先打印n:${n}的循环值,而是先打印 x:${x}的值。含义就是先定义一个x,x的内容为 循环list,每次循环渲染显示的内容 n:${n} 的值 为x中的一个元素,全部循环完,x的值也组装完了,结果打印显示x:${x}。记住不是说list为x中的list,即不是为x注入一个list(这 就是为啥最后打印x时前边都有n:而不是和原list内容一样的原因,表明此list不是x中的属性,即此list和x没什么关系,此仅借用一下list机制而已), 而是通过list机制 进行循环得出临时值作为x的元素。 20、setting指令,用于动态设置freeMarker的运行环境: 该指令用于设置FreeMarker的运行环境,该指令的语法格式如下:<#setting name=value>,在这个格式中,name的取值范围包含如下几个: locale:该选项指定该模板所用的国家/语言选项 number_format:指定格式化输出数字的格式 boolean_format:指定两个布尔值的语法格式,默认值是true,false date_format,time_format,datetime_format:指定格式化输出日期的格式 time_zone:设置格式化输出日期时所使用的时区 21、<#return> 用于退出宏的运行 22、调用Java方法: 如: public class A implements TemplateMethodModelEx{ @Override public Object exec(List arg0) throws TemplateModelException { return new SimpleNumber(((SimpleScalar)arg0.get(1)).getAsString().indexOf(((SimpleScalar)arg0.get(0)).getAsString())); } 或 public TemplateModel exec(List arg0) throws TemplateModelException { return new SimpleNumber(((String)arg0.get(1)).indexOf((String)arg0.get(0))); } } 封装数据模型时用map模型(如果用vo对象模型的话可能需要vo中有个map,此map的效果等同于直接用map模型),直接 map.put("method",new A()); method可以任意变量名 由于此要put一个对象,所以此map模型用到的map就不能用Map<String,String>了,即不用带泛型,用 Map root = new HashMap(); 下边就是到模板了,模板里表达式格式为:<#assign x = "something"> ${method("met",x)} 即先定义一个变量x,然后调用表达式method("met",x),此表达式传2个 参数,当解析模板执行到此表达式时会将此表达式的2个参数直接传到A类中的exec方法中的参数,exec方法参数为list,那模板中传几个参数都是以String类型或 SimpleScalar(ftl包装string用的对象,可以调用getAsString()得到string内容)类型直接传到exec的参数list中的,传几个过来就都封装到这个list中,exec执行 结果返回给模板 ${结果} 渲染显示出来。 说明:调用Java方法时需要java类实现TemplateMethodModel 或 TemplateMethodModelEx接口,但是好像会覆盖掉属性的访问,即这个类中的其他属性可能就没法访问了(这个是否能访问没测过) 即要调用的java方法所在类 必须实现TemplateMethodModel接口,即实现 exec方法 即 public TemplateModel exec(List arg0) throws TemplateModelException 或 public Object exec(List list) throws TemplateModelException 方法。 在模板解析时模板中调用表达式时会立即调用到java类中的上边实现的exec方法,exec方法返回值 返回给 模板中表达式处 渲染显示出来。 -->