freemarker模板解析过程

例如:一个freemarker表达式<body> ${hello} </body>,会被解析成三个部分,分别是
<body>
${hello}
</body>
前面和后面的body标签,在freemarker中被定义为TextBlock,中间的变量定义为DollarVariable。那么目前的结构也就是RootExpression = TextBlock DollarVariable TextBlock。解释器一进来将会对RootExpression进行解析,RootExpression将会依次调用TextBlock DollarVariable TextBlock进行解析。不同类型将会做不同操作,根据传进来的Context参数进行相应赋值并输出等。
当Template启动解释时,由Environment进入调用根元素的访问动作,根元素会依次访问所包含的TemplateElement,直到所有叶子节点访问完成,这些访问动作是通过调用Environment的visit方法控制,Environment做些相关必要操作,再根据访问的节点类型调用相应节点的访问操作。当访问到包含需要解释器的元素节点时,则会启动解释器做解释操作,根据Expression类型,调用getStringValue,并传入参数Environment,相应类型的表达式根据Environment解释得到输入字符串的值,返回并写到响应流,即解释完成。

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
操作字符串内置函数
1.substring(start,end)从一个字符串中截取子串
start:截取子串开始的索引,start必须大于等于0,小于等于end
end: 截取子串的长度,end必须大于等于0,小于等于字符串长度,如果省略该参数,默认为字符串长度。
2.cap_first 将字符串中的第一个单词的首字母变为大写。
3.uncap_first将字符串中的第一个单词的首字母变为小写。
4.capitalize将字符串中的所有单词的首字母变为大写
5.date,time,datetime将字符串转换为日期
注意:如果指定的字符串格式不正确将引发错误
6.ends_with 判断某个字符串是否由某个子串结尾,返回布尔值
注意:布尔值必须转换为字符串才能输出
7.html 用于将字符串中的<、>、&和"替换为对应得<>&quot:&amp
8.index_of(substring,start)在字符串中查找某个子串,返回找到子串的第一个字符的索引,如果没有找到子串,则返回-1。
Start参数用于指定从字符串的那个索引处开始搜索,start为数字值。
如果start大于字符串长度,则start取值等于字符串长度,如果start小于0,则start取值为0。
9.length返回字符串的长度
10.lower_case将字符串转为小写
11.upper_case将字符串转为大写
12.contains 判断字符中是否包含某个子串。返回布尔值
注意:布尔值必须转换为字符串才能输出
13.number将字符串转换为数字
14.replace用于将字符串中的一部分从左到右替换为另外的字符串。
15.split使用指定的分隔符将一个字符串拆分为一组字符串
16.trim 删除字符串首尾空格
操作数字内置函数
1.c 用于将数字转换为字符串
2.string用于将数字转换为字符串
Freemarker中预订义了三种数字格式:number,currency(货币)和percent(百分比)其中number为默认的数字格式转换
操作布尔值内置函数
string用于将布尔值转换为字符串输出
true转为"true",false转换为"false"
foo?string("yes","no")如果布尔值是true,那么返回"yes",否则返回no

freemarker日志实现过程分析

freemarker有自己的log类,这是一个抽象类,具体的日志打印委托给classpath里面合适的日志jar包来执行,寻找合适日志jar的查找顺序是:Apache Log4J, Apache Avalon LogKit, JDK log。如果一个合适的日志实现类都没有找到,日志功能将被抑制,并会使用System.err打印出错误提示信息。
如果我们想自己指定使用的日志类型,那么可以通过:
Loger.selectLoggerLibrary(int library);
注意:一定要在freemarker初始化阶段进行设置,在调用任何freemarker api之前进行设置,否则freemarker将会与默认的日志实现进行绑定,从而自己指定的日志修改将不会起到作用。

Freemarker中大于号>的使用

在Freemarker中,比较数据的大小时候,要注意大于号(>)的使用。如果不注意,程序就会发生异常信息,如下面的例子:

 
1
2
3
4
<#assign x = 4>
<#if x>5 >
     x >5
</#if>

以上的方式进行比较,就会发生异常,原因是Freemarker内部的解析处理原因,x>5中的大于号将会跟<#if中的小于号进行配对,导致解析出现问题。针对这种情况,有两种方式解决:
方法一:加上括号。

 
1
2
3
4
<#assign x = 4>
<#if (x>5) >
     x > 5
</#if>

方法二:使用gt符号。

 
1
2
3
4
<#assign x = 4>
<#if x gt 5 >
     x > 5
</#if>

总结一下:

使用>=和>的时候有一点小问题。FreeMarker解释>的时候可以把它当作FTL标签的结束符。为了避免这种问题,不得不将表达式放到括号内:<#if (x > y) >,另外,可以使用lt代替<,lte代替<=,gt代替>,gte代替>=。由于历史遗留的原因,FTL也支持\lt,\lte,\gt和\gte,使用他们和使用不带反斜杠的效果一样。

序列的重点知识小结

(1)序列的默认值为[],看下面的例子:

<#if (winnersList![])?size gt 0>
    <table class="winner_table" border="0" cellspacing="0" cellpadding="0">
        <tr>
            <th class="bdr_gray">中奖账号</th>
            <th>猜测差值</th>
        </tr>
    <#list winnersList as list>
        <tr>
            <td class="bdr_gray">${list.accountId!""}</td>
            <td>${list.deviation!""}</td>
        </tr>
    </#list>
    </table>
</#if>
说明:在上面例子中,winnersList默认为[],它的内建函数为size

(2)序列的连接:

可以将两个序列连接成一个新的序列,连接序列的运算符是'+',见下面的例子:
<#list ["一","二","三"] + ["四","五","六"] as x>
    ${x}
</#list>
输出结果如下:
一二三四五六

(3)序列的切分:

举个例子看序列的切分应用场景:有的时候我们在页面中不需要显示那么长的字符串,比如新闻标题,这样用下面的例子就可以自定义显示的长度
<#if title.content?length lt 8>
           <a href>${title.content?default("")}</a>
      <#else>
           <a href title="${title.content}">${title.content[0..3]?default("")}</a>
</#if>
上面例子的作用是:如果这个字符串的长度小于8,那么就正常显示,反之则取4位。
序列的切分要注意下面两点:
从FreeMarker 2.3.3版本以后lastindex才能省略。
如果试图访问一个序列首变量之前的项或末变量之后的项将会引起错误,模板的执行也会中断。

(4)子序列的定义:

序列中的项是表达式,那么也可以这样做:[2 + 2, [1, 2, 3, 4], "what"],其中第一个子变量是数字4,第二个子变量是一个序列,第三个子变量是字符串"what"。

(5)数字序列的定义:

第一种定义序列的方式:
<#assign nums=[1,2,3,4,5,77,8,99]/>
使用list指令将序列输出,
<#list nums as num>
   ${num}
</#list>
第二种定义序列的方式
定义了一个连续的序列,
<#assign nums=12..99/>
这种方式定义的序列的内容是12到99
说明:
从上面的例子可以看出,序列也可以用start..end定义存储数字范围的序列,这里的start和end是处理数字值表达式,比如2..5和[2, 3, 4, 5]是相同的,但是使用前者会更有效率(内存占用少而且速度快)。可以看出前者也没有使用方括号,这样也可以用来定义递减的数字范围,比如5..2。(此外,还可以省略end,只需5..即可,但这样序列默认包含5,6,7,8等递增量直到无穷大)。

(6)判断序列是否包含某个元素

如果要判断序列中是否包含某个指定的元素,可以使用序列的内建函数seq_contains。
注:seq_contains这个内建函数从FreeMarker 2.3.1 版本开始可用。而在2.3 版本中不存在。
<#--声明一个序列,包含若干个元素-->
<#assign x = ["red", 16, "blue", "cyan"]>
<#--使用seq_contains判断序列中的元素是否存在-->
"blue": ${x?seq_contains("blue")?string("yes", "no")}
"yellow": ${x?seq_contains("yellow")?string("yes", "no")}
16: ${x?seq_contains(16)?string("yes", "no")}
"16": ${x?seq_contains("16")?string("yes", "no")}
输出结果:
"blue": yes
"yellow": no
16: yes
"16": no
附:seq_前缀在这个内建函数中是需要的,用来和contains 区分开。contains函数用来在字符串中查找子串(因为变量可以同时当作字符串和序列)。

StringTemplateLoader的用法

作为一个模板框架,freemarker的功能还是很强大的。在模板处理方面,freemarker有多种形式,最常见的方式是将模板文件放在一个统一的文件夹下面,如下形式:
Configuration cfg = new Configuration();
cfg.setDirectoryForTemplateLoading(new File("templates"));
如果我想把模板存放到数据库中,可以实现吗?答案是肯定的。在这里可以使用StringTemplateLoader来加载模板内容。主要的代码实现如下所示:
Configuration cfg = new Configuration(); 
StringTemplateLoader stringLoader = new StringTemplateLoader();  
String templateContent="hello ${name}!";  
stringLoader.putTemplate("myTemplate",templateContent);  
cfg.setTemplateLoader(stringLoader);  
Template template = cfg.getTemplate("myTemplate","utf-8");

freemarker报错的处理方案

freemarker文件如果出错,网站的前台页面会报出很明显的错误-焦黄的背景,血红的文字,很不利于用户体验的。如何修改这个问题呢?
首先需要在struts.xml配置文件里添加下面一行代码:

 
1
<constant name="struts.freemarker.manager.classname" value="net.swiftlet.freemarker.MyFreemarkerManager" />

接着新建MyFreemarkerManager类,如下所示:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyFreemarkerManager extends org.apache.struts2.views.freemarker.FreemarkerManager
{
    private static final Logger LOG = LoggerFactory.getLogger(MyFreemarkerManager.class);
    public void init(ServletContext servletContext) throws TemplateException
    {
        config = createConfiguration(servletContext);
        config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        contentType = DEFAULT_CONTENT_TYPE;
 
        wrapper = createObjectWrapper(servletContext);
        if (LOG.isDebugEnabled())
        {
            LOG.debug("Using object wrapper of class " + wrapper.getClass().getName());
        }
        config.setObjectWrapper(wrapper);
        templatePath = servletContext.getInitParameter(INITPARAM_TEMPLATE_PATH);
        if (templatePath == null)
        {
            templatePath = servletContext.getInitParameter("templatePath");
        }
        configureTemplateLoader(createTemplateLoader(servletContext, templatePath));
        loadSettings(servletContext);
    }
}

Freemarker常用技巧(三)的更多相关文章

  1. Freemarker常用技巧

    1,截取字符串 有的时候我们在页面中不需要显示那么长的字符串,比如新闻标题,这样用下面的例子就可以自定义显示的长度 < lt. <= lte. > gt. >= gte < ...

  2. Freemarker常用技巧(二)

    1 list.break指令<#list sequence as item>  ...</#list>tem_index:当前变量的索引值.item_has_next:是否存在 ...

  3. Freemarker常用技巧(一)

    1 截取字符串有的时候我们在页面中不需要显示那么长的字符串,比如新闻标题,这样用下面的例子就可以自定义显示的长度<#if title.content?length lt 8>        ...

  4. Linux Shell常用技巧(三)

    八. 流编辑器sed: sed一次处理一行文件并把输出送往屏幕.sed把当前处理的行存储在临时缓冲区中,称为模式空间(pattern space).一旦sed完成对模式空间中的行的处理,模式空间中的行 ...

  5. 【shell 大系】Linux Shell常用技巧

    在最近的日常工作中由于经常会和Linux服务器打交道,如Oracle性能优化.我们数据采集服务器的资源利用率监控,以及Debug服务器代码并解决其效率和稳定性等问题.因此这段时间总结的有关Linux ...

  6. Linux Shell常用技巧(目录)

    Linux Shell常用技巧(一) http://www.cnblogs.com/stephen-liu74/archive/2011/11/10/2240461.html一. 特殊文件: /dev ...

  7. Freemarker的常用技巧总结

    Freemarker的常用技巧总结 Freemarker视频教程 1,截取字符串 有的时候我们在页面中不需要显示那么长的字符串,比如新闻标题,这样用下面的例子就可以自定义显示的长度 < lt. ...

  8. oracle存储过程常用技巧

    我们在进行pl/sql编程时打交道最多的就是存储过程了.存储过程的结构是非常的简单的,我们在这里除了学习存储过程的基本结构外,还会学习编写存储过程时相关的一些实用的知识.如:游标的处理,异常的处理,集 ...

  9. Vim 常用技巧:

    Vim 常用技巧: 将回车由默认的8个空格改为4个空格: 命令:set sw=4 修改tab为4空格: 命令:set ts=4 设置每一级的缩进长度: 命令:set shiftwidth=4 设置文件 ...

随机推荐

  1. 你好,C++(4)2.1.3 我的父亲母亲:编译器和链接器 2.1.4 C++程序执行背后的故事

    2.1.3  我的父亲母亲:编译器和链接器 从表面上看,我是由Visual Studio创建的,而实际上,真正负责编译源代码创建生成可执行程序HelloWorld.exe的却是Visual Studi ...

  2. 【NOI2006】最大获利

    [问题描述] 新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是挑战.THU 集团旗下的CS&T 通讯公司在新一代通讯技术血战的前夜,需要做太多的准备工作,仅就站址选择一项,就 ...

  3. Python学习6.1_函数参数及参数传递

    大多数编程语言都绕不开一个名词,那就是--函数(function).而函数很重要的部分则是参数(arguments)的使用.Python的参数传递总体来说是根据位置,传递对应的参数.阐述如下: 1.位 ...

  4. Android中AsyncTask异步

    今天我们学习了 AsyncTack, 这是一个异步任务. 那么这个异步任务可以干什么呢? 因为只有UI线程,即主线程可以对控件进行更新操作.好处是保证UI稳定性,避免多线程对UI同时操作. 同时要把耗 ...

  5. 转:Netty系列之Netty高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用 ...

  6. SCALA常规练习A

    package com.hengheng.scala import scala.util.control.Breaks._ import scala.util.Random object Applic ...

  7. 启动Activity但不显示其界面

    最近在工作中做了一个很简单的任务,制作一个apk,点击该app链接到某一个网站.     代码很简单,只有如寥寥几行: (browser.java) package com.test.browser; ...

  8. Facebook 开源 AI 所使用的硬件平台 'Big Sur'

    Facebook 开源 AI 所使用的硬件平台 'Big Sur' Facebook 今开源其 AI 所使用的硬件平台 'Big Sur'.'Big Sur' 是兼容开放机架的 GPU 加速硬件平台. ...

  9. 【转】 android 4.4 Step Counter Sensor计步器的使用

    原文网址:http://blog.csdn.net/aikongmeng/article/details/40457233 版权声明:本文为博主原创文章,未经博主允许不得转载. Android 官方参 ...

  10. 矢量做图组件OTGisX的使用(类似Mapbase)

    一:组件添加到工具栏 要在应用程序中应用OTGisX控件,首先要把所下载的OTGisX组件添加到.Net工程中.并将其添加到工具箱托盘中.添加方式为:在工具箱上单击右键,选择“选择项”,会出现“选择工 ...