Freemarker 特性 和 注意事项(做项目时遇到的记录的)

来源:互联网 发布:算法导论中文版pdf下载 编辑:程序博客网 时间:2024/06/04 22:47
先借鉴javaeye Robbin的文章:FreeMarker三宗罪! 
http://www.iteye.com/topic/17468 

引用

FreeMarker是Quake Wang推荐我使用的。刚学FreeMarker的时候,发现freemarker真的很棒!简单易用,功能强大。但是用它做了几个项目以后开始不爽了。 

一宗罪:freemarker的变量必须有值,没有被赋值的变量就会抛出异常,那个黄黄的freemarker出错页面,真是让人看了太难过了。 
freemarker的FAQ上面冠冕堂皇的说,未赋值的变量强制抛错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误。但是实际的效果是:带来的是非常大的编程麻烦,程序里面几乎所有可能出现空值的变量统统需要加上${xxx?if_exists},有些循环条件还需要写if判断,这样不但没有杜绝应该杜绝的错误,反而极大增加了编程的麻烦。 

二宗罪:freemarker的map限定key必须是string,其他数据类型竟然无法操作!这一点就不讲了,JavaEye上面已经有人抱怨过了。连Webwork的开发人员Pat Lightboy都在抱怨这一点。 


三宗罪:freemarker为了编程方便把不可序列化的东西往session里面放! 
freemarker支持在页面里面直接操作Session,request等,例如${Session[...]},方便确实很方便,但是一旦需要做群集,就会报错。 
今天是b051问起我这个问题,他在做Tomcat群集的时候发现freemarker报错,HttpSessionHashModel不可序列化。他修改该类源代码,让他实现序列化接口,仍然报错。我一看,HttpSessionHashModel包含的属性: 

Java代码  收藏代码
  1. private HttpSession session;    
  2. private final ObjectWrapper wrapper;    
  3.     
  4. // These are required for lazy initializing session    
  5. private final FreemarkerServlet servlet;    
  6. private final HttpServletRequest request;    
  7. private final HttpServletResponse response;   


登时晕倒,这样的东西还往Session里面放?bad smell! 
严重警告应用需要往群集上面发布应用的同学们,千万别用freemarker! 

不过瑕不掩瑜,freemarker也是有优点的: 

1、易学易用 
我是看了一天文档就用得挺熟练了,freemarker文档写得太好了,例子丰富,照做一遍全都会了。 

2、功能强大 
比Velocity强大多了,还支持JSP Tag。不过最有意义的是macro功能,可以自定义常用的macro,实现页面常规操作的可复用性。 

3、报错信息友好 
很多应用服务器的JSP报错信息是无法定位到源代码行的。不过freemarker报错定位很准确,丝毫不差,而且信息丰富,一看就知道怎么回事 (虽然那个黄黄的页面看起来让人难受) 



看了很多关于freemarker的评论,自己也实际项目中使用了一下,总结了一些我需要注意的地方,记录下来: 
1:freemarker 对于空值的判断。 freemarker接受的对象如果是空,会直接报错。这里不管它这样设计好坏,至少我觉得我的代码里面会有很多判断空值的语句。尤其是其上一层对象出现空的时候更要判断为空的情况,单纯字符串对象你可以使用 ${obj!""}来判断obj是否为空,如果为空赋值为空字符串。使用<#if obj??>判断obj是否为空。 

2:freemarker接受的map对象key只能是string类型的。 

3:freemarker的自定义函数。 这个自定义函数还是很有用的,可以在freemarker接受到后台请求自定义方法后再次进入到后台进行计算。 
Java代码  收藏代码
  1. ${methodModel('${tname}','${row}')}   //methodModel 自定义的freemarker方法的类对象  在后台绑定    
  2.   
  3. componentMap.put(EnumConstants.FREEMARKER_CUSTOMIZE_METHOD_METHODMODEL.toString(), new GridMethodModel());  

常量就是methodModel 这个freemarker请求的类在后台通过map绑定的key-name. 
让我们看看这个类里面怎么接受这2个参数和处理这个逻辑 
Java代码  收藏代码
  1. public class GridMethodModel implements TemplateMethodModel {  
  2.   
  3.     @SuppressWarnings("unchecked")  
  4.     @Override  
  5.     public Object exec(List arg) throws TemplateModelException {  
  6.           
  7.         Row row = null ;  
  8.         try {  
  9.             row = RowDocument.Factory.parse(arg.get(1).toString().replaceAll("xml-fragment""per:row")).getRow();  
  10.         } catch (XmlException e) {  
  11.             // TODO Auto-generated catch block  
  12.             e.printStackTrace();  
  13.         }  
  14.         String modelName = arg.get(0).toString();  
  15.           
  16.           
  17.         Model[] models = row.getModelArray();  
  18.         for(Model m : models)  
  19.         {  
  20.             if(modelName.equals(m.getName()))  
  21.                 return m.getValue();  
  22.         }  
  23.           
  24.         return "";  
  25.     }  
  26.   
  27. }  


4:freemarker几个判断类型的函数 和 简单的宏使用 
引用

<#macro parse results> 
<#if results?is_enumerable> 
[<#list results as result><@parse results=result/><#if result_has_next>,</#if></#list>] 
<#elseif results?is_hash_ex> 
{<#list results?keys as key>"${key?j_string}":<@parse results=results[key]/><#if key_has_next>,</#if></#list>} 
<#elseif results?is_date>"${results?string("yyyy-MM-dd")}" 
<#elseif results?is_boolean>"${results?string}" 
<#elseif results?is_number>"${results?c}" 
<#elseif results?is_string>"${results?j_string}" 
</#if> 
</#macro> 
<@compress single_line=true><@parse results=results/></@compress> 



5:freemarker内建函数 
引用

Sequence的内置函数 

1.     sequence?first 返回sequence的第一个值。 

2.     sequence?last  返回sequence的最后一个值。 

3.     sequence?reverse 将sequence的现有顺序反转,即倒序排序 

4.     sequence?size    返回sequence的大小 

5.     sequence?sort    将sequence中的对象转化为字符串后顺序排序 

6.     sequence?sort_by(value) 按sequence中对象的属性value进行排序 

注意:Sequence不能为null。 

Hash的内置函数 

1. hash?keys 返回hash里的所有key,返回结果为sequence 

2. hash?values 返回hash里的所有value,返回结果为sequence 

操作布尔值
 

string 用于将布尔值转换为字符串输出 

true转为“true”,false转换为“false” 

foo?string(“yes”,”no”)如果布尔值是true,那么返回“yes”,否则返回no 

操作数字 

1.c 用于将数字转换为字符串 

${123?c} à结果为123 

2.string用于将数字转换为字符串 

Freemarker中预订义了三种数字格式:number,currency(货币)和percent(百分比)其中number为默认的数字格式转换 

例如: 

<#assign tempNum=20> 

${tempNum}    

${tempNum?string.number}或${tempNum?string(“number”)} à结果为20 

${tempNum?string.currency}或${tempNum?string(“currency”)} à结果为¥20.00 

${tempNum?string. percent}或${tempNum?string(“percent”)} à结果为2,000% 


freemarker 的内建函数 contains 的使用: 

<#if employee.departments?contains(department)>checked="checked"</#if> 

其中departments是一个集合,而department是departments集合里的一个元素。contains函数可以判断出,元素 department是否存在于集合departments里,最终返回一个Boolean 


Sequence内置的分段器: chunk 
用途:某些比较BT的排版需求 
模板: 

Java代码  收藏代码
  1. <#assign seq = ['a''b''c''d''e''f''g''h''i''j']>   
  2. <#list seq?chunk(4) as row>   
  3. <ul>   
  4. <li><#list row as cell>${cell} </#list></li>   
  5. </ul>   
  6. </#list>   
  7.   
  8. <#list seq?chunk(4'-') as row>   
  9. <tr>   
  10. <td><#list row as cell>${cell} </#list></td>   
  11. </tr>   
  12. </#list>   



输出: 
引用

<ul> 
<li>a</li> 
<li>b</li> 
<li>c</li> 
<li>d</li> 
</ul> 
<ul> 
<li>e</li> 
<li>f</li> 
<li>g</li> 
<li>h</li> 
</ul> 
<ul> 
<li>i</li> 
<li>j</li> 
</ul> 

<tr> 
<td>a</td> 
<td>b</td> 
<td>c</td> 
<td>d</td> 
</tr> 
<tr> 
<td>e</td> 
<td>f</td> 
<td>g</td> 
<td>h</td> 
</tr> 
<tr> 
<td>i</td> 
<td>j</td> 
<td>-</td> 
<td>-</td> 
</tr>


原文地址:http://ericxiong.iteye.com/blog/849063


0 0