Freemarker
应用场景
对比 JSP Freemarker Velocity
在java领域,表现层技术主要有三种:jsp、freemarker、velocity。
JSP
jsp是大家最熟悉的技术
优点:
1、功能强大,可以写java代码
2、支持jsp标签(jsp tag)
3、支持表达式语言(el)
4、官方标准,用户群广,丰富的第三方jsp标签库
5、性能良好。jsp编译成class文件执行,有很好的性能表现
缺点:
jsp没有明显缺点,非要挑点骨头那就是,由于可以编写java代码,如使用不当容易破坏mvc结构
Velocity
velocity是较早出现的用于代替jsp的模板语言
优点:
1、不能编写java代码,可以实现严格的mvc分离
2、性能良好,据说比jsp性能还要好些
3、使用表达式语言,据说jsp的表达式语言就是学velocity的
缺点:
1、不是官方标准
2、用户群体和第三方标签库没有jsp多。
3、对jsp标签支持不够好
freemarker
优点:
1、不能编写java代码,可以实现严格的mvc分离
2、性能非常不错
3、对jsp标签支持良好
4、内置大量常用功能,使用非常方便
5、宏定义(类似jsp标签)非常方便
6、使用表达式语言
缺点:
1、不是官方标准
2、用户群体和第三方标签库没有jsp多
选择freemarker的原因
1、性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。
2、宏定义比jsp tag方便
3、内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便
4、支持jsp标签
5、可以实现严格的mvc分离
6、jsp和freeMarker是两种不同的技术,或者说不通的理念。
jsp需要访问的时候访问Dao层抓取数据,生成html页面,返给浏览器。
freeMarker则是提前根据模板生成好html的静态页面,这样在访问时,直接访问到的就是一个静态页面,这个就是效率问题了,不过freeMarker适合用变化不大,但是内容很多的网页。
7. 相比较FreeMarker而言,Velocity更加简单、轻量级,但它的功能却没有FreeMarker那么强大。
FreeMarker Introduction
FreeMarker是一款模板引擎,是一个Java类库,用来生成html页面把页面静态化。
模板编写为FreeMarker Template Language(FTL)。
原理:模板页面=》解析标签和替换变量=》html页面
https://freemarker.apache.org/
2017-11-03 2.3.27
2016-06-26 2.3.25
2015-09-02 2.3.24-pre01 GNU=>Apache Incubation
http://fm-classic.sourceforge.net/
2004-06-15 2.2.8
2003-02-08 2.1.5
2002-06-12 2.0.3
1999-01-03 1.0
https://archive.apache.org/dist/incubator/freemarker/engine
Freemarker-2.3.25
Reference
Doc
- https://freemarker.apache.org/docs/index.html
- https://sourceforge.net/projects/freemarker/files/chinese-manual/
Java API
- https://freemarker.apache.org/docs/api/index.html
- https://freemarker.apache.org/docs/ref_directive_list.html
- https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
FreeMarker模板开发指南
基本结构
插值:${...} interpolation(插值)。
标签:预定义标签以#开头;用户自定义的FTL标签使用@来代替#。
注释: <#-- -->。不像HTML注释<!-- -->那样,FTL注释不会出现在访问者的页面中。
Note:
- Anything not an FTL tag or an interpolation or comment is considered static text and will not be interpreted by FreeMarker; it is just printed to the output as-is.
- FTL是区分大小写的。
- 预定义指令可以不要 # , 不建议这样用。
- 可以在FTL标签和FTL注释中, 使用 [ 和 ] 来代替 < 和 >。
表达式
- 直接指定值
- 检索变量
- 字符串操作
- 序列操作
- 哈希表操作
- 连接:
passwords + { "joe": "secret42" }
- 连接:
- 算术运算:
(x * 1.5 + 10) / 2 - y % 100
- 比较运算:
x == y
,x != y
,x < y
,x > y
,x >= y
,x <= y
,x lt y
,x lte y
,x gt y
,x gte y
, 等等。。。。。。 - 逻辑操作:
!registered && (firstVisit || fromEurope)
- 内建函数:
name?upper_case
,path?ensure_starts_with('/')
- 方法调用:
repeat("What", 3)
- 处理不存在的值:
- 赋值操作:
=
,+=
,-=
,*=
,/=
,%=
,++
,--
Note:
- 原生字符串:在原生字符串中, 反斜杠和 ${ 没有特殊含义, 它们被视为普通的字符。在开始的引号或单引号之前放置字母r,${r"C:\foo\bar"}
- 科学记数法暂不支持使用 (1E3 就是错误的), 而且也不能在小数点之前不写0(.5 也是错误的)。
- 值域表达式:start..end,start..<end, start..!end, start..*length, start..
变量
子变量
用.(点)来访问
空变量
FreeMarker不能引用不存在的变量,
一个不存在的变量和一个是null值的变量,对于FreeMarker来说是一样的。
可以指定一个默认值来避免变量丢失,在变量名后面跟着一个 !(感叹号)和默认值: ${user!"visitor"}
可以在变量名后面通过放置??来询问一个变量是否存在
<#if user??>
<h1>Welcome ${user}!</h1>
</#if>
多级访问的变量
animals.python.price!0 vs. (animals.python.price)!0
命名空间
通常来说,变量因为名称冲突时也会相互冲突。 所以要为每个库中的变量使用不同的命名空间。
简单的情况下可以只使用一个命名空间,称之为 主命名空间。
import vs. include: <#import "/lib/my_test.ftl" as my>
Directives 指令|标签 介绍
if
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
<#else>
...
</#if>
list, items, sep,else
<#list misc.fruits>
<p>Fruits:
<ul>
<#items as fruit>
<li>${fruit}<#sep> and</#sep>
</#items>
</ul>
<#else>
<p>We have no fruits.
</#list>
include可以在模板中插入另外一个FreeMarker模板文件
<#include "/copyright_footer.html">
数值类型
标量scalars:string, number, boolean, date|time
字符串只是字符的任意序列,不能将它用于计算目的,也不能和其它数字进行比较等等。
使用保留字 true 和 false 来指定布尔值。
受到Java平台的限制,FreeMarker 有时是不能决定日期的部哪分被使用。
FTL并没有独立的字符类型, 获取字符${user[0]}。
Containers: hash, sequence, collections
集合是有限制的序列。不能获取集合的大小, 也不能通过索引取出集合中的子变量,但是它们仍然可以通过 list
指令来遍历。
序列(集合)中的变量可以是任意类型的, 这些变量也可以是哈希表,序列(或集合)。
Built-ins 内建函数|方法
在数值上添加
animal.name?cap_first
常见函数
- user?html给出user的HTML转义版本,比如&会由&来代替。
- user?upper_case给出user值的大写版本(比如“JOHN DOE”来替代“John Doe”)
- animal.name?cap_first给出animal.name的首字母大写版本(比如“Mouse”来替代“mouse”)
- user?length给出user值中字符的数量(对于“John Doe”来说就是8)
- animals?size给出animals序列中项目的个数
list内使用
<#list animals as animal>…</#list>
l animal?index给出了在animals中基于0开始的animal的索引值
l animal?counter也像index,但是给出的是基于1的索引值
l animal?item_parity基于当前计数的奇偶性,给出字符串“odd”或“even”。在给不同行着色时非常有用,比如在<td class="${animal?item_parity}Row">中。
带参数的内建函数
l string: animal.protected?string("Y", "N")基于animal.protected的布尔值来返回字符串“Y”或“N”。
l animal?item_cycle('lightRow','darkRow')是item_parity更为常用的变体形式。
l fruits?join(", ")通过连接所有项,将列表转换为字符串,在每个项之间插入参数分隔符(比如“orange,banana”)
l user?starts_with("J")根据user的首字母是否是“J”返回布尔值true或false。
内建函数应用可以链式操作
user?upper_case?html
Freemarker自定义标签Directives
自定义指令用 macro 指令来定义
<#macro name param1 param2 ... paramN>
...
<#nested loopvar1, loopvar2, ..., loopvarN>
...
<#return>
...
</#macro>
Java实现
public class UpperDirective implements TemplateDirectiveModel
Freemarker自定义函数Function
ftl定义:function, return
<#function name param1 param2 ... paramN>
...
<#return returnValue>
...
</#function>
Java实现
public class IndexOfMethod implements TemplateMethodModelEx
所有内建函数
字符串内建函数
字符串属性:length, index_of, last_index_of,
字符串替换: replace, trim, cap_first, uncap_first, capitalize, chop_linebreak, keep_after, keep_after_last, keep_before, keep_before_last, left_pad, right_pad, lower_case, remove_beginning, remove_ending, ensure_ends_with, ensure_starts_with,
字符串判断:contains, starts_with, ends_with, boolean, matches, groups
字符串转换:split, word_list, number, date, time, datetime, string, url, url_path, html, xhtml, xml, j_string, js_string, json_string, rtf,
通用标志:i, f, r, m, s, c
支持使用这些通用标志的内建函数:replace, split, matches, keep_after, keep_after_last, keep_before, keep_before_last, ensure_starts_with
数字内建函数
abs, string, round, floor, ceiling, c,is_infinite,is_nan,lower_abc, upper_abc,
日期内建函数
string, date, time, datetime, date_if_unknown, time_if_unknown, datetime_if_unknown, iso_...(iso_utc, iso_local, …)
${openingTime?string["yyyy-MM-dd hh:mm:ss a"]}
布尔值内建函数
then, c, string
三元运算符: booleanExp?then(whenTrue, whenFalse)
序列内建函数
size, seq_contains, seq_index_of, seq_last_index_of, sort, sort_by, join, chunk, first, last, reverse,
哈希表内建函数
keys, values
XML结点内建函数
ancestors, children, node_name, node_namespace, node_type, parent, root
循环变量内建函数
counter, has_next, index, is_even_item, is_first, is_last, is_odd_item, item_cycle, item_parity, item_parity_cap
这些内建函数只能用于list 和 items 指令 的循环变量 (也可以用于已经废弃的 foreach 指令)。
Type independent built-ins
switch
priority?switch(1, "low", 2, "medium", 3, "high"),或者是 true?switch(priority <= 1, "low", priority == 2, "medium", priority >= 3, "high")。
很少使用的和专家级的内建函数
has_content, is_...(is_sring, is_number, is_date_like, is_time, is_datetime, …), eval,
byte, double, float, int, long, short
number_to_date, number_to_time, number_to_datetime
所有指令: <#xxx …>…</#xxx>, <#xxx…>,
- l assign, global, local, setting
- l import, include
- l if, else, elseif
- l list, else, items, sep, break
- l switch, case, default, break
- l attempt, recover
- l visit, recurse, fallback: xml
- l escape, noescape
- l function, return
- l macro, nested, return
- l compress
- l flush
- l ftl
- l noparse, stop
- l nt, t, lt, rt
- l User-defined directive (<@...>)
FTL 中的保留名称
- true:布尔值"true"
- false:布尔值"false"
- gt:比较运算符"大于"
- gte:比较运算符"大于或等于"
- lt:比较运算符"小于"
- lte:比较运算符"小于或等于"
- as:由少数指令使用
- in:由少数指令使用
- using:由少数指令使用
程序开发指南
开发步骤
- 创建 Configuration 实例
- 创建数据模型
- 获取模板
- 合并模板和数据模型
Configuration
配置(configuration)就是 freemarker.template.Configuration 对象, 它存储了常用(全局,应用程序级)的设置,定义了想要在所有模板中可用的变量(称为共享变量)。
应用程序典型的用法是使用一个独立的共享 Configuration 实例。
Shared variables
Settings