----------------------------------------------------------模板开发指南-----------------------------------------------------------
1 如果user或name为null,使用默认值。对pojo的调用方式与哈希表相同
${(user.name)!"anonymous"} == ${(user["name"])!"anonymous"}
2 数字格式化插值 # {...}, 如果为空 ! 使用默认值
#{(user.age)!100}
3 调用函数
${"anonymous"?upper_case}
4 截取字符串,建议使用?substring()
<#-- 同样适用于list。但${"anonymous"[5..2]}不适用于字符串,可用于list-->
${"anonymous"[0]} ${"anonymous"[0..4]} ${"anonymous"[5..]}
5 user.name?? 判断是否存在, 布尔不能直接打印,可使用?string("true","false") 。true/false代表布尔值,不需引号
${(user.name)???string("true","false")}
6 \ 转义
${"It's \"quoted\" and this is a backslash: \\"}
7 r"...",对字符串不进行解析,直接输出
${r"${foo}"}
8 ${08} == ${+8} == ${8.00} == ${8}
9 list支持合并、截取
<#list ([1,"haha",4]+[3,"heihei"])[3..1] as x>
${x}
</#list>
10 数字列表
<#list 3..1 as x>
${x}
</#list>
11 哈希表支持合并,覆盖。assign 定义全局变量,local 定义局部变量
<#assign map={"one":"张三","two":"李四"}+{"three":"王五","two":"刘六"}>
${map.two} == ${map["two"]}
<#list map?keys as key>
${key} -- ${map[key]}
</#list>
12 获取复杂变量类型,不支持基本类型、string、date
${user.class.simpleName}
13 字符串链接
${"Hello ${user.name}!"} ${"Hello " + user.name + "!"}
14 freemarker只有SimpleNumber类型 这一种数据类型。
${5/2} = 2.5 ${(5/2)?int} = 2 ${(5/2)?round} = 3 ${(5/2)?floor} = 2 ${(5/2)?ceiling} = 3
15 比较符使用 >等,或使用(),遗留问题也可使用\gt。数字、日期类型都可使用比较符。 ?date用于指定使用日期的date/time/datetime
<#if (user.age > 20)>${user.name}</#if> <#if (user.birthday?date < .now?date)>${user.name}</#if>
16 内建函数
html:将html特殊字符进行转换
cap_first/lower_case/upper_case
size:序列/哈希表元素数量,字符串长度使用length
17 调用已定义过的方法
<#--${repeat("love",3)} -->
18 escape 相当于所有$ {..}都变为$ {..?html},noescape 可抵消其作用
<#escape x as x?html>
...
<p>Name: ${user.name}</p>
<p>Age:
<#noescape>${user.age}</#noescape>
</p>
<h2>Hobbys:</h2>
<#list user.hobbys as hobby>
<div class="comment">
${hobby}
</div>
</#list>
...
</#escape>
19 宏变量 greet是宏变量名,person是参数。无默认值的参数调用时必须传入,传入的参数可是变量。< #nested>引用嵌套内容,";"后是循环变量("循环变量"这个名称有歧义),通过< #nested>传入到嵌套内容中
<#macro greet person color="black">
<font size="+2"> ${person} ${color}</font>
嵌套内容:
<#nested "a","b","c">
</#macro>
调用:
<@greet person="handsome">嘛哩嘛哩哄</@greet>
//传入color
<@greet person=user.name color="red"/>
//传入循环变量,获取< #nested "a","b","c">中定义的值
<@greet person=user.name ; a,b> ${a} ${b} </@greet>
20 变量作用域。局部同名变量会隐藏普通变量。在局部可使用.globals来调用数据模型的同名变量(map.add()添加的数据)
<#assign x = "plain">
1. ${x} <#-- 这里是普通变量 -->
<#macro test>
2. ${x} <#-- 这里我们仍然看到的是普通变量 -->
<#local x = "local">
3. ${x} <#-- 现在局部变量隐藏了它 -->
<#list ["loop"] as x>
4. ${x} <#-- 现在循环变量隐藏了局部变量 -->
</#list>
5. ${x} <#-- 现在又可以看到局部变量了 -->
</#macro>
<@test/>
6. ${x} <#-- 普通变量的值没有被改变 -->
<#list ["loop"] as x>
7. ${x} <#-- 现在循环变量隐藏了普通变量 -->
<#assign x = "plain2"> <#-- 替换普通变量, 隐藏在这里不起作用-->
8. ${x} <#-- 它仍然隐藏普通变量 -->
</#list>
9. ${x} <#-- 普通变量的新值 -->
-----------------------
内部循环可隐藏外部的变量
<#list ["loop 1"] as x>
${x}
<#list ["loop 2"] as x>
${x}
<#list ["loop 3"] as x>
${x}
</#list>
${x}
</#list>
${x}
</#list>
21 import 使用其他模板文件中的定义的宏函数、变量,不会把模板的内容包含进来。(include指令会将引入的模板当作本模板的一部分来处理,视为一个文件)
<#import "import.ftl" as im>
<@im.copyright .now?date/>
${im.mail}
//替换im命名空间下的普通变量
<#assign mail="simth" in im>
${im.mail}
22 剥离空白
//compress是针对输出的内容进行压缩,移除前后的空白,中间的多个空白只保留一个。single_line=true会移除所有的换行符
<@compress single_line=true>
前面有一个缩进, 后面有空白
前面有很多空格
</@compress>
//可使用t、rt、lt来移除空白
23 通过在模板文件的首行添加[#ftl],可替换文件中的< , >。之后可使用[ #list]、[ #if]来代替< #list>、< #if>
----------------------------------------------------------程序开发指南-----------------------------------------------------------
1 入门级实例:
Configuration cfg = new Configuration();
cfg.setDirectoryForTemplateLoading(new File("resource/template"));
cfg.setObjectWrapper(new DefaultObjectWrapper());
Template temp = cfg.getTemplate(ftl);
Writer out = new OutputStreamWriter(System.out);
temp.process(new HashMap(), out);
out.flush();
//Template 只创建一次,缓存在Configuration中,默认5秒检测模板是否更新,基于类的模板加载器不会注意到模板文件内容的改变,vm会回收模板,cfg.clearTemplateCache可清空
//可通过cfg.setSetting(cache_storage,"strong:20,soft:250")或调用cfg.setCacheStorage来配置缓存,
//默认使用MruCacheStorage("strong:0,soft:Integer.max_value"),实现二级缓存。一级缓存中的模板引用次数超过阀值就被加载到二级缓存中
2 每个接口都继承TemplateModel,此处只列出最常使用的一个实现类
数字:TemplateNumberModel {getAsNumber}----------SimpleNumber
字符串:TemplateScalarModel {getAsString}-----------SimpleScalar
日期:TemplateDateModel {getAsDate/getDateType}-----------SimpleDate ==>(getAsDate/getDateType)
布尔:TemplateBooleanModel-----------BooleanModel.true/BooleanModel.FALSE
序列:TemplateSequenceModel {get/size}-----------SimpleSequence
哈希表:TemplateHashModel {get/isEmpty}-----------SimpleHash
集合:TemplateCollectionModel {iterator}-----------SimpleCollection
方法:TemplateMethodModel {exec}-----------自定义类,返回值是void的方法返回TemplateModel.NOTHING
指令:TempateDirectiveModel {execute}-----------自定义类
3 自定义指令,使用@来调用,可通过map.add()来添加到模板,也可通过< #assign upper="com.example.UpperDirective"?new()>来添加
< @myDirective count=3;cnt> $ {cnt} Welcome </ @myDirective>
public void execute(
Environment env, //通过env.getOut()对象来输出内容。
Map params, //参数
TemplateModel[] loopVars,//代码中填充loopVars变量,freemarker会自动将变量设置到嵌套内容中
TemplateDirectiveBody body //嵌套内容
) throws TemplateException, IOException {
4 map.add()添加的是java对象,模板实现会通过ObjectWrapper使用合适的TemplateModel对象来替换原有对象
5 共享变量集合初始化时,会自动添加capture_output/compress/html_escape/normalize_newliens/xm_escape。
cfg.setSharedVariable("wrap",new WrapDirective());//TemplateModel不是线程安全的,不能在多线程中作为共享变量
cfg.setSharedVariable("company","Foo Inc.");
6 配置信息(可在Configuration、Template、Environment中配置,后者会覆盖前者)
Configuration: cfg.setLocale/cfg.setNumberFormat、cfg.setSetting(name,value)
Template: 由TemplateCache来管理,第一次使用就设置了配置信息,然后就将Template视为只读的
Environment:e nv.setLocale/env.setNumberFormat 或在模板文件中 < #setting locale="cz">
7 模板加载器
setDirectoryForTemplateLoading(File dir)
setClassForTemplateLoading(Class cl, String prefix) //使用 Class.getResource()获取
setServletContextForTemplateLoading(Object servletContext, String path) //使用ServletContext.getResource()获取
cfg.setTemplateLoader(
new MultiTemplateLoader(new TemplateLoader[] { //从多个位置加载
FileTemplateLoader,
URLTemplateLoader, //通过url访问
ClassTemplateLoader
}));
8 模板异常
cfg.setTemplateExceptionHandler(TemplateExceptionHandler):只要出现TemplateException异常,便会调用配置的模板异常处理器
freemarker的错误控制器:
TemplateExceptionHandler.DEBUG_HANDLER(默认):打印异常信息,重新抛出异常
TemplateExceptionHandler.HTML_DEBUG_HANDLER:除了异常信息格式,与DEBUG_HANDLER相同
TemplateExceptionHandler.IGNORE_HANDLER:忽略异常(只捕获不处理)
TemplateExceptionHandler.RETHROW_HANDLER:只简单重新抛出
9 变量
//Template.process会在内部创建一个Environment,
//Environment存储模板执行是的运行状态信息,还会存储assign、macro、local、global变量
//freeMarker查找变量顺序:Environment-->process传入的数据模型-->Configuration中的共享变量集合
10 freemarker采用unicode(UTF-16)字符集,可通过encoding配置项或cfg.setEncoding方法设置模板字符集。模板使用的字符集和生成的输出内容的字符集是独立的
可通过output_encoding配置输出字符集,也可通过env.setOutputEncoding对模板单独设置字符集
11 使用DefaultObjectWrapper不允许Collection、Map在模板执行期间被修改,但BeansWrapper包装器允许
a:所有Bean都被包装成TemplateHashModel。BeansWrapper.setExposeFields(true)会暴露公有非静态变量,作为哈希表的键值
b:BeansWrapper有一些安全级别
EXPOSE_SAFE(默认):适用于大多数应用
EXPOSE_ALL:允许调用不安全的方法
EXPOSE_PROPERTIES_ONLY:只暴露bean属性的getter方法
EXPOSE_NOTHING:不会暴露任何属性和方法
12 解包:在模板中调用java方法是,传入的参数需要从模板模型转换会java对象
空模型 -------> null
AdapterTemplateModel -------> model.getAdaptedObejct() :由BeansWrapper创建的所有方法是AdapterTemplateModel的实现
WrapperTemplateModel(已废弃) -------> model.getWrappedObject()
TemplateScalarModel -------> String/Character
TemplateNumberModel -------> Number
TemplateBooleanModel -------> Boolean
TemplateHashModel -------> Map
TemplateSequenceModel -------> List/数组
TemplateCollectionModel -------> Set/Collection/Iterable
TemplateDateModel -------> Date
....(FreeMarker教程 4.4.8)
13 访问静态方法
BeansWrapper.getStaticModels()返回哈希表模型,通过该对象可访问所有java的静态变量($ {staticModel["java.lang.System"].currentTimeMills()})
为了安全,可staticModel.get("java.io.File")获取后,放入到数据模型对象中
BeanSWrapper.getEnumModels()返回哈希模型,通过该对象可访问所有枚举($ {enumModel["java.math.RoundingMode"].UP})
14 日志
FreeMarker会按 slf4j、commons logging、log4j、avalon、logging顺序查找日志包,使用第一个发现的
Logger.selectLoggerLibarary(LoggerLIBRARY_SLF4J):可通过传入Logger.LIBRARY_NONE来关闭日志
freemarker记录器(freemarker.*):beans/cache/runtime/runtime.attempt/servlet/jsp
15 FreeMarkerServlet:会在数据模型中放置几个web相关的哈希表:Request、Session、Application、RequestParameters
可覆盖Servlet的preTemplateProcess来添加额外的数据模型
< @include_pate path="" [inherit_params=true] [params={}]>
a、和 <jsp:include page="">作用相同,
b、inherit_params用于设置被包含的页面能否访问当前请求中的参数
c、params用于设置包含页面能访问的参数
//freemarker模板中使用jsp标签
< #assign html=JspTaglibs["/WEB-INF/struts-html.tld"]>
a、使用jsp标签
b、global指令创建的变量相当于page。jsp需要访问的其他域数据都放置在数据模型中
freemarkerServlet实现了自身轻量级的JSP运行时环境,用到JSP标签,但并不依赖于JSP支持。
需要在TLD文件中,开启FreeMarker的JSP运行时环境来分发事件到JSP标签库注册事件监听器
<listener>
<listener-class>freemarker.ext.jsp.EventForwarding</listener-class>
</listener>
在JSP中嵌入ftl,嵌入的ftl可以访问jsp的4种范围内的属性
----------------------------------------------------------参考文档-----------------------------------------------------------
1 字符串
cap_first|capitalize:首字母大写
uncap_first:首字母小写
lower_case
upper_case
left_pad(5[,"-"]):距左边
right_pad
date("MM/dd/yyyy"):将字符串转换为日期
time("HH:mm:ss")
datetime("yyyy-MM-dd hh:mm a")
index_of("",from)
last_index_of("",from)
contains
start_with:以指定字符串开始
ends_with:
group与matches:正则表达式匹配
html
rft:\-->\\、 {-->\{、 }-->\}
url(["utf-8"])
j_string:java规则字符串转义
js_string:js规则字符串转义
json_string:json规则字符串转义
chop_linebreak:末尾没有换行符的字符串,可以换行,否则不改变字符串
substring(from[,toExclusive]):截取
trim
split
word_list:以空格来分隔字符串
replace
length
number:格式化
2 数字
//格式化,可通过< #setting number_format="currency">来设置全局
string.number
string.currency
string.percent
string.computer:相当于c,不带格式的数字
//会四舍五入
string("0")
string("0.#")
string("0000.00")
string("0.##E0")
round
floor
ceiling
3 日期
//格式化,可通过date_format/time_format/datetime_format
//将日期转换成字符串
string.short
string.medium
string.long
string.full
string("yyyy-MM-dd")
string("HH:mm:ss")
string("yyyy-MM-dd HH:mm:ss")
//返回的仍然是SimpleDate类型
datetime
date
time
iso_utc
iso_utc_ms
iso_local
iso("UTC")
iso_m("GMT+02")
4 布尔
string("true","false")
5 序列
first
last
seq_contains
seq_index_of
seq_last_index_of
sort
sort_by("key"):序列中存放的是哈希表,以哈希表中的指定key来排序
reverse
size
chunk(4[,'-']):将序列分隔成大小为4的子序列,小于4的用指定字符('-')填充
6 哈希表
keys:键集合
values:值集合
7 高级函数
//将毫秒值转换成时间
number_date
number_to_time
number_to_datetime
eval:对字符串进行运算,"1+2"?eval 返回3
interpret:将字符串解释为ftl模板
has_content:变量不为null,且不为空,返回true
//类型判断
is_string
is_number
is_boolean
is_date
is_sequence
is_hash/is_hash_ex
is_collection
is_directive
is_method
is_macro
is_enumerable
is_transform
is_node
is_indexable
namespace:命名空间
new():创建实现了TemplateModel接口的类
--------------------------------------------------------
1 <#if condition>
<#elseif condition>
...
<#else>
...
</#if>
2 <#switch value>
<#case refValue1>
...
<#break>
<#case refValue2>
...
<#break>
<#case refValueN>
...
<#break>
<#default>
...
</#switch>
3 <#list 1..x as i>
<#if x = "spring"><#break></#if>
</#list>
4 <#include path [options]>:
options:
encoding:默认子文件从父文件集成编码方式
parse:如果false,则子文件将被视为简单文本
<#include "*/footer.ftl">:从当前目录或它的任意父目录获取,会按如下顺序查找:
foot_en_US.ftl --> foot_en.ftl --> foot.ftl
可通过cfg.setlocalizedLookup关闭本地查找
5 <#import path as hash> 引入一个库,可视为java中的import
同一个path多次调用,只会实例化一个,其他命名空间都指向这一个实例
6 <#noparse>...</#noparse>:不解析,原文本输出
7 移除空白:
compress:对输出内容压缩,移除首尾空格符,连续出现的空格只保留一个
t:移除首尾空格符
lt:
rt:
nt:抵消t/lt/rt作用
8 <#escape identifier as expression>
...
<#noescape>...</#noescape>
...
</#escape>
9 <#assign name=value>
or
<#assign name1=value1 name2=value2 ... nameN=valueN>
or
<#assign same as above... in namespacehash>
or
<#assign name>
capture this
</#assign>
or
<#assign name in namespacehash>
capture this
</#assign>
//不应该向嵌套内容中插入变量
<#assign x>Hello ${user}!</#assign>:错误
<#assign x="Hello ${user}!">:正确
10 //所有命名空间都可见
<#global name=value>
or
<#global name1=value1 name2=value2 ... nameN=valueN>
or
<#global name>
capture this
</#global>
11 //在宏和方法中定义
<#local name=value>
or
<#local name1=value1 name2=value2 ... nameN=valueN>
or
<#local name>
capture this
</#local>
12 <#setting name=value>,可设置的值:locale/number_format/boolean_format/url_escaping_charset/classic_compatible
/date_format/time_format/datetime_format/time_zone
13 <#macro name param1 param2 ... paramN>
...
<#nested loopvar1, loopvar2, ..., loopvarN>
...
<#return>
...
</#macro>
14 //类似于macro,但是没有nested,使用${name(params)}来调用
<#function name param1 param2 ... paramN>
...
<#return returnValue>
...
</#function>
15 flush:简单调用Writer的flush
16 <#stop reason>:终止模板处理
17 <#ftl param1=value1 param2=value2 ... paramN=valueN>:参数应该是
encoding:模板编码
strip_whitespace:开启/关闭空白剥离
strip_text:开启时,当模板被解析时模板中所有顶级文本被移除
strict_syntax:严格语法,也可调用cfg.setStrictSyntaxMode
ns_prefixes:关联节点命名空间前缀的哈希表
attributes:关联模板任意属性的哈希表
--------------------------------------------
特殊变量:
data_model:直接访问数据模型的哈希表(在global定义的变量同名时使用)
globals:访问全局变量的哈希表
locales:访问本地变量的哈希表
locale:返回当前本地设置的值
language:返回本地设置的语言部分的值
main:访问主命名空间的哈希表
namespace:访问当前命名空间的哈希表
now:返回当前时间
output_encoding:返回当前输出字符集的名称
template_name:当前模板的名称
url_escaping_charset:如果存在,它存储了用于URL转义的字符集名称
vars:表达式.vars.foo或.vars["foo"]和foo效果一样,当变量名比较特殊时使用
version:freemarker的版本