`

freemarker 概述

 
阅读更多
FreeMarker概述
       FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写
       FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式的应用程序
       虽然FreeMarker具有一些编程的能力,但通常由Java程序准备要显示的数据,由FreeMarker生成页面,通过模板显示准备的数据(如下图)

       FreeMarker不是一个Web应用框架,而适合作为Web应用框架一个组件
       FreeMarker与容器无关,因为它并不知道HTTP或Servlet;FreeMarker同样可以应用于非Web应用程序环境
       FreeMarker更适合作为Model2框架(如Struts)的视图组件,你也可以在模板中使用JSP标记库
       FreeMarker是免费的

1、通用目标
       能够生成各种文本:HTML、XML、RTF、Java源代码等等
       易于嵌入到你的产品中:轻量级;不需要Servlet环境
       插件式模板载入器:可以从任何源载入模板,如本地文件、数据库等等
       你可以按你所需生成文本:保存到本地文件;作为Email发送;从Web应用程序发送它返回给Web浏览器

2、强大的模板语言
       所有常用的指令:include、if/elseif/else、循环结构
       在模板中创建和改变变量
       几乎在任何地方都可以使用复杂表达式来指定值
       命名的宏,可以具有位置参数和嵌套内容
       名字空间有助于建立和维护可重用的宏库,或者将一个大工程分成模块,而不必担心名字冲突
       输出转换块:在嵌套模板片段生成输出时,转换HTML转义、压缩、语法高亮等等;你可以定义自己的转换

3、通用数据模型
       FreeMarker不是直接反射到Java对象,Java对象通过插件式对象封装,以变量方式在模板中显示
       你可以使用抽象(接口)方式表示对象(JavaBean、XML文档、SQL查询结果集等等),告诉模板开发者使用方法,使其不受技术细节的打扰

4、为Web准备
       在模板语言中内建处理典型Web相关任务(如HTML转义)的结构
       能够集成到Model2 Web应用框架中作为JSP的替代
       支持JSP标记库
       为MVC模式设计:分离可视化设计和应用程序逻辑;分离页面设计员和程序员

5、智能的国际化和本地化
       字符集智能化(内部使用UNICODE)
       数字格式本地化敏感
       日期和时间格式本地化敏感
       非US字符集可以用作标识(如变量名)
       多种不同语言的相同模板

6、强大的XML处理能力
       <#recurse> 和<#visit>指令(2.3版本)用于递归遍历XML树
       在模板中清楚和直觉的访问XML对象模型


FreeMarker设计指南(1)



 
1、快速入门
(1)模板 + 数据模型 = 输出
       FreeMarker基于设计者和程序员是具有不同专业技能的不同个体的观念
       他们是分工劳动的:设计者专注于表示——创建HTML文件、图片、Web页面的其它可视化方面;程序员创建系统,生成设计页面要显示的数据
       经常会遇到的问题是:在Web页面(或其它类型的文档)中显示的信息在设计页面时是无效的,是基于动态数据的
       在这里,你可以在HTML(或其它要输出的文本)中加入一些特定指令,FreeMarker会在输出页面给最终用户时,用适当的数据替代这些代码
       下面是一个例子:
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
  <p>Our latest product:
  <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
       这个例子是在简单的HTML中加入了一些由${…}包围的特定代码,这些特定代码是FreeMarker的指令,而包含FreeMarker的指令的文件就称为模板(Template)
       至于user、latestProduct.url和latestProduct.name来自于数据模型(data model)
       数据模型由程序员编程来创建,向模板提供变化的信息,这些信息来自于数据库、文件,甚至于在程序中直接生成
       模板设计者不关心数据从那儿来,只知道使用已经建立的数据模型
       下面是一个可能的数据模型:
(root)
  |
  +- user = "Big Joe"
  |
  +- latestProduct
      |
      +- url = "products/greenmouse.html"
      |
      +- name = "green mouse"
       数据模型类似于计算机的文件系统,latestProduct可以看作是目录,而user、url和name看作是文件,url和name文件位于latestProduct目录中(这只是一个比喻,实际并不存在)
       当FreeMarker将上面的数据模型合并到模板中,就创建了下面的输出:
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome Big Joe!</h1>
  <p>Our latest product:
  <a href="products/greenmouse.html">green mouse</a>!
</body>
</html>
(2)数据模型
       典型的数据模型是树型结构,可以任意复杂和深层次,如下面的例子:
(root)
  |
  +- animals
  |   |
  |   +- mouse
  |   |   |  
  |   |   +- size = "small"
  |   |   |  
  |   |   +- price = 50
  |   |
  |   +- elephant
  |   |   |  
  |   |   +- size = "large"
  |   |   |  
  |   |   +- price = 5000
  |   |
  |   +- python
  |       |  
  |       +- size = "medium"
  |       |  
  |       +- price = 4999
  |
  +- test = "It is a test"
  |
  +- whatnot
      |
      +- because = "don't know"
       类似于目录的变量称为hashes,包含保存下级变量的唯一的查询名字
       类似于文件的变量称为scalars,保存单值
       scalars保存的值有两种类型:字符串(用引号括起,可以是单引号或双引号)和数字(不要用引号将数字括起,这会作为字符串处理)
       对scalars的访问从root开始,各部分用“.”分隔,如animals.mouse.price
       另外一种变量是sequences,和hashes类似,只是不使用变量名字,而使用数字索引,如下面的例子:
(root)
  |
  +- animals
  |   |
  |   +- (1st)
  |   |   |
  |   |   +- name = "mouse"
  |   |   |
  |   |   +- size = "small"
  |   |   |
  |   |   +- price = 50
  |   |
  |   +- (2nd)
  |   |   |
  |   |   +- name = "elephant"
  |   |   |
  |   |   +- size = "large"
  |   |   |
  |   |   +- price = 5000
  |   |
  |   +- (3rd)
  |       |
  |       +- name = "python"
  |       |
  |       +- size = "medium"
  |       |
  |       +- price = 4999
  |
  +- whatnot
      |
      +- fruits
          |
          +- (1st) = "orange"
          |
          +- (2nd) = "banana"
       这种对scalars的访问使用索引,如animals[0].name
(3)模板
       在FreeMarker模板中可以包括下面三种特定部分:
       ${…}:称为interpolations,FreeMarker会在输出时用实际值进行替代
       FTL标记(FreeMarker模板语言标记):类似于HTML标记,为了与HTML标记区分,用#开始(有些以@开始,在后面叙述)
       注释:包含在<#--和-->(而不是<!--和-->)之间
       下面是一些使用指令的例子:
       if指令
<#if animals.python.price < animals.elephant.price>
  Pythons are cheaper than elephants today.
<#else>
  Pythons are not cheaper than elephants today.
</#if>
       list指令
<p>We have these animals:
<table border=1>
  <tr><th>Name<th>Price
  <#list animals as being>
  <tr><td>${being.name}<td>${being.price} Euros
  </#list>
</table>
输出为:
<p>We have these animals:
<table border=1>
  <tr><th>Name<th>Price
  <tr><td>mouse<td>50 Euros
  <tr><td>elephant<td>5000 Euros
  <tr><td>python<td>4999 Euros
</table>
       include指令
<html>
<head>
  <title>Test page</title>
</head>
<body>
  <h1>Test page</h1>
  <p>Blah blah...
<#include "/copyright_footer.html">
</body>
</html>
       一起使用指令
<p>We have these animals:
<table border=1>
  <tr><th>Name<th>Price
  <#list animals as being>
  <tr>
    <td>
      <#if being.size = "large"><b></#if>
      ${being.name}
      <#if being.size = "large"></b></#if>
    <td>${being.price} Euros
  </#list>
</table>

FreeMarker设计指南(3)


   

 
3、模板
(1)整体结构
       模板使用FTL(FreeMarker模板语言)编写,是下面各部分的一个组合:
       文本:直接输出
       Interpolation:由${和},或#{和}来限定,计算值替代输出
       FTL标记:FreeMarker指令,和HTML标记类似,名字前加#予以区分,不会输出
       注释:由<#--和-->限定,不会输出
       下面是以一个具体模板例子:
<html>[BR]
<head>[BR]
  <title>Welcome!</title>[BR]
</head>[BR]
<body>[BR]
  <#-- Greet the user with his/her name -->[BR]
  <h1>Welcome ${user}!</h1>[BR]
  <p>We have these animals:[BR]
  <ul>[BR]
  <#list animals as being>[BR]
    <li>${being.name} for ${being.price} Euros[BR]
  </#list>[BR]
  </ul>[BR]
</body>[BR]
</html>
       [BR]是用于换行的特殊字符序列
       注意事项:
       FTL区分大小写,所以list是正确的FTL指令,而List不是;${name}和${NAME}是不同的
       Interpolation只能在文本中使用
       FTL标记不能位于另一个FTL标记内部,例如:
<#if <#include 'foo'>='bar'>...</if>
       注释可以位于FTL标记和Interpolation内部,如下面的例子:
<h1>Welcome ${user <#-- The name of user -->}!</h1>[BR]
<p>We have these animals:[BR]
<ul>[BR]
<#list <#-- some comment... --> animals as <#-- again... --> being>[BR]
...
       多余的空白字符会在模板输出时移除
(2)指令
       在FreeMarker中,使用FTL标记引用指令
       有三种FTL标记,这和HTML标记是类似的:
       开始标记:<#directivename parameters>
       结束标记:</#directivename>
       空内容指令标记:<#directivename parameters/>
       有两种类型的指令:预定义指令和用户定义指令
       用户定义指令要使用@替换#,如<@mydirective>...</@mydirective>(会在后面讲述)
       FTL标记不能够交叉,而应该正确的嵌套,如下面的代码是错误的:
<ul>
<#list animals as being>
  <li>${being.name} for ${being.price} Euros
  <#if use = "Big Joe">
     (except for you)
</#list>
</#if> <#-- WRONG! -->
</ul>
       如果使用不存在的指令,FreeMarker不会使用模板输出,而是产生一个错误消息
       FreeMarker会忽略FTL标记中的空白字符,如下面的例子:
<#list[BR]
  animals       as[BR]
     being[BR]
>[BR]
${being.name} for ${being.price} Euros[BR]
</#list    >
       但是,<、</和指令之间不允许有空白字符
(3)表达式
       直接指定值
       字符串
       使用单引号或双引号限定
       如果包含特殊字符需要转义,如下面的例子:
${"It's \"quoted\" and
this is a backslash: \\"}
 
${'It\'s "quoted" and
this is a backslash: \\'} 
输出结果是:
It's "quoted" and
this is a backslash: \
 
It's "quoted" and
this is a backslash: \ 
       下面是支持的转义序列:
转义序列 含义
\" 双引号(u0022)
\' 单引号(u0027)
\\ 反斜杠(u005C)
\n 换行(u000A)
\r Return (u000D)
\t Tab (u0009)
\b Backspace (u0008)
\f Form feed (u000C)
\l <
\g >
\a &
\{ {
\xCode 4位16进制Unicode代码
       有一类特殊的字符串称为raw字符串,被认为是纯文本,其中的\和{等不具有特殊含义,该类字符串在引号前面加r,下面是一个例子:
${r"${foo}"}
${r"C:\foo\bar"}
输出的结果是:
${foo}
C:\foo\bar
       数字
       直接输入,不需要引号
       精度数字使用“.”分隔,不能使用分组符号
       目前版本不支持科学计数法,所以“1E3”是错误的
       不能省略小数点前面的0,所以“.5”是错误的
       数字8、+8、08和8.00都是相同的
       布尔值
       true和false,不使用引号
       序列
       由逗号分隔的子变量列表,由方括号限定,下面是一个例子:
<#list ["winter", "spring", "summer", "autumn"] as x>
${x}
</#list> 
输出的结果是:
winter
spring
summer
autumn
       列表的项目是表达式,所以可以有下面的例子:
[2 + 2, [1, 2, 3, 4], "whatnot"]
       可以使用数字范围定义数字序列,例如2..5等同于[2, 3, 4, 5],但是更有效率,注意数字范围没有方括号
       可以定义反递增的数字范围,如5..2
       散列(hash)
       由逗号分隔的键/值列表,由大括号限定,键和值之间用冒号分隔,下面是一个例子:
{"name":"green mouse", "price":150}
       键和值都是表达式,但是键必须是字符串
       获取变量
       顶层变量: ${variable},变量名只能是字母、数字、下划线、$、@和#的组合,且不能以数字开头
       从散列中获取数据
       可以使用点语法或方括号语法,假设有下面的数据模型:
(root)
|
+- book
|   |
|   +- title = "Breeding green mouses"
|   |
|   +- author
|       |
|       +- name = "Julia Smith"
|       |
|       +- info = "Biologist, 1923-1985, Canada"
|
+- test = "title" 
下面都是等价的:
book.author.name
book["author"].name
book.author.["name"]
book["author"]["name"]
       使用点语法,变量名字有顶层变量一样的限制,但方括号语法没有该限制,因为名字是任意表达式的结果
       从序列获得数据:和散列的方括号语法语法一样,只是方括号中的表达式值必须是数字;注意:第一个项目的索引是0
       序列片断:使用[startIndex..endIndex]语法,从序列中获得序列片断(也是序列);startIndex和endIndex是结果为数字的表达式
       特殊变量:FreeMarker内定义变量,使用.variablename语法访问
       字符串操作
       Interpolation(或连接操作)
       可以使用${..}(或#{..})在文本部分插入表达式的值,例如:
${"Hello ${user}!"}
${"${user}${user}${user}${user}"}
       可以使用+操作符获得同样的结果
${"Hello " + user + "!"}
${user + user + user + user}
       ${..}只能用于文本部分,下面的代码是错误的:
<#if ${isBig}>Wow!</#if>
<#if "${isBig}">Wow!</#if>
应该写成:
<#if isBig>Wow!</#if>
       子串
       例子(假设user的值为“Big Joe”):
${user[0]}${user[4]}
${user[1..4]}
结果是(注意第一个字符的索引是0):
BJ
ig J 
       序列操作
       连接操作:和字符串一样,使用+,下面是一个例子:
<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
- ${user}
</#list>
输出结果是:
- Joe
- Fred
- Julia
- Kate
       散列操作
       连接操作:和字符串一样,使用+,如果具有相同的key,右边的值替代左边的值,例如:
<#assign ages = {"Joe":23, "Fred":25} + {"Joe":30, "Julia":18}>
- Joe is ${ages.Joe}
- Fred is ${ages.Fred}
- Julia is ${ages.Julia}
输出结果是:
- Joe is 30
- Fred is 25
- Julia is 18
       算术运算
       +、-、×、/、%,下面是一个例子:
${x * x - 100}
${x / 2}
${12 % 10}
输出结果是(假设x为5):
-75
2.5
2
       操作符两边必须是数字,因此下面的代码是错误的:
${3 * "5"} <#-- WRONG! -->
       使用+操作符时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串,例如:
${3 + "5"}
输出结果是:
35
       使用内建的int(后面讲述)获得整数部分,例如:
${(x/2)?int}
${1.1?int}
${1.999?int}
${-1.1?int}
${-1.999?int}
输出结果是(假设x为5):
2
1
1
-1
-1
       比较操作符
       使用=(或==,完全相等)测试两个值是否相等,使用!= 测试两个值是否不相等
       =和!=两边必须是相同类型的值,否则会产生错误,例如<#if 1 = "1">会引起错误
       Freemarker是精确比较,所以对"x"、"x  "和"X"是不相等的
       对数字和日期可以使用<、<=、>和>=,但不能用于字符串
       由于Freemarker会将>解释成FTL标记的结束字符,所以对于>和>=可以使用括号来避免这种情况,例如<#if (x > y)>
       另一种替代的方法是,使用lt、lte、gt和gte来替代<、<=、>和>=
       逻辑操作符
       &&(and)、||(or)、!(not),只能用于布尔值,否则会产生错误
       例子:
<#if x < 12 && color = "green">
  We have less than 12 things, and they are green.
</#if>
<#if !hot> <#-- here hot must be a boolean -->
  It's not hot.
</#if>
       内建函数
       内建函数的用法类似访问散列的子变量,只是使用“?”替代“.”,下面列出常用的一些函数
       字符串使用的:
       html:对字符串进行HTML编码
       cap_first:使字符串第一个字母大写
       lower_case:将字符串转换成小写
       upper_case:将字符串转换成大写
       trim:去掉字符串前后的空白字符
       序列使用的:
       size:获得序列中元素的数目
       数字使用的:
       int:取得数字的整数部分(如-1.9?int的结果是-1)
       例子(假设test保存字符串"Tom & Jerry"):
${test?html}
${test?upper_case?html}
输出结果是:
Tom &amp; Jerry
TOM &amp; JERRY
       操作符优先顺序
操作符组 操作符
后缀 [subvarName] [subStringRange] . (methodParams)
一元 +expr、-expr、!
内建 ?
乘法 *、 / 、%
加法 +、-
关系 <、>、<=、>=(lt、lte、gt、gte)
相等 ==(=)、!=
逻辑and &&
逻辑or ||
数字范围 ..
(4)Interpolation
       Interpolation有两种类型:select userid count
       通用Interpolation:${expr}
       数字Interpolation:#{expr}或#{expr; format}
       注意:Interpolation只能用于文本部分
       通用Interpolation
       插入字符串值:直接输出表达式结果
       插入数字值:根据缺省格式(由#setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string格式化单个Interpolation,下面是一个例子:
<#setting number_format="currency"/>
<#assign answer=42/>
${answer}
${answer?string}  <#-- the same as ${answer} -->
${answer?string.number}
${answer?string.currency}
${answer?string.percent} 
输出结果是:
$42.00
$42.00
42
$42.00
4,200%
       插入日期值:根据缺省格式(由#setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string格式化单个Interpolation,下面是一个使用格式模式的例子:
${lastUpdated?string("yyyy-MM-dd HH:mm:ss zzzz")}
${lastUpdated?string("EEE, MMM d, ''yy")}
${lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'")}
输出的结果类似下面的格式:
2003-04-08 21:24:44 Pacific Daylight Time
Tue, Apr 8, '03
Tuesday, April 08, 2003, 09:24:44 PM (PDT)
       插入布尔值:根据缺省格式(由#setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string格式化单个Interpolation,下面是一个例子:
<#assign foo=true/>
${foo?string("yes", "no")}
输出结果是:
yes
       数字Interpolation的#{expr; format}形式可以用来格式化数字,format可以是:
       mX:小数部分最小X位
       MX:小数部分最大X位
       例子:
           <#-- If the language is US English the output is: -->
<#assign x=2.582/>
<#assign y=4/>
#{x; M2}   <#-- 2.58 -->
#{y; M2}   <#-- 4    -->
#{x; m1}   <#-- 2.6 -->
#{y; m1}   <#-- 4.0 -->
#{x; m1M2} <#-- 2.58 -->
#{y; m1M2} <#-- 4.0  --> 

FreeMarker设计指南(4)


   

 
4、杂项
(1)用户定义指令
       宏和变换器变量是两种不同类型的用户定义指令,它们之间的区别是宏是在模板中使用macro指令定义,而变换器是在模板外由程序定义,这里只介绍宏
       基本用法
       宏是和某个变量关联的模板片断,以便在模板中通过用户定义指令使用该变量,下面是一个例子:
<#macro greet>
  <font size="+2">Hello Joe!</font>
</#macro>
       作为用户定义指令使用宏变量时,使用@替代FTL标记中的#
<@greet></@greet>
       如果没有体内容,也可以使用:
<@greet/>
       参数
       在macro指令中可以在宏变量之后定义参数,如:
<#macro greet person>
  <font size="+2">Hello ${person}!</font>
</#macro> 
       可以这样使用这个宏变量:
<@greet person="Fred"/> and <@greet person="Batman"/> 
输出结果是:
  <font size="+2">Hello Fred!</font>
and   <font size="+2">Hello Batman!</font>

       宏的参数是FTL表达式,所以下面的代码具有不同的意思:
<@greet person=Fred/>
       这意味着将Fred变量的值传给person参数,该值不仅是字符串,还可以是其它类型,甚至是复杂的表达式
       宏可以有多参数,下面是一个例子:
<#macro greet person color>
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro> 
       可以这样使用该宏变量:
<@greet person="Fred" color="black"/> 
       其中参数的次序是无关的,因此下面是等价的:
<@greet color="black" person="Fred"/>
       只能使用在macro指令中定义的参数,并且对所有参数赋值,所以下面的代码是错误的:
<@greet person="Fred" color="black" background="green"/>
<@greet person="Fred"/>
       可以在定义参数时指定缺省值,如:
<#macro greet person color="black">
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>
       这样<@greet person="Fred"/>就正确了
       宏的参数是局部变量,只能在宏定义中有效
       嵌套内容
       用户定义指令可以有嵌套内容,使用<#nested>指令执行指令开始和结束标记之间的模板片断
       例子:
<#macro border>
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    <#nested>
  </tr></td></table>
</#macro>
这样使用该宏变量:
<@border>The bordered text</@border>
输出结果:
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    The bordered text
  </tr></td></table>

       <#nested>指令可以被多次调用,例如:
<#macro do_thrice>
  <#nested>
  <#nested>
  <#nested>
</#macro>
<@do_thrice>
  Anything.
</@do_thrice>
输出结果:
  Anything.
  Anything.
  Anything. 
       嵌套内容可以是有效的FTL,下面是一个有些复杂的例子:
<@border>
  <ul>
  <@do_thrice>
    <li><@greet person="Joe"/>
  </@do_thrice>
  </ul>
</@border> 
输出结果:
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
      <ul>
    <li><font size="+2">Hello Joe!</font>
 
    <li><font size="+2">Hello Joe!</font>
 
    <li><font size="+2">Hello Joe!</font>
 
  </ul>
 
  </tr></td></table>
       宏定义中的局部变量对嵌套内容是不可见的,例如:
<#macro repeat count>
  <#local y = "test">
  <#list 1..count as x>
    ${y} ${count}/${x}: <#nested>
  </#list>
</#macro>
<@repeat count=3>${y?default("?")} ${x?default("?")} ${count?default("?")}</@repeat>
输出结果:
    test 3/1: ? ? ?
    test 3/2: ? ? ?
    test 3/3: ? ? ?
        
       在宏定义中使用循环变量
       用户定义指令可以有循环变量,通常用于重复嵌套内容,基本用法是:作为nested指令的参数传递循环变量的实际值,而在调用用户定义指令时,在<@…>开始标记的参数后面指定循环变量的名字
       例子:
<#macro repeat count>
  <#list 1..count as x>
    <#nested x, x/2, x==count>
  </#list>
</#macro>
<@repeat count=4 ; c, halfc, last>
  ${c}. ${halfc}<#if last> Last!</#if>
</@repeat>
输出结果:
  1. 0.5
  2. 1
  3. 1.5
  4. 2 Last!

       指定的循环变量的数目和用户定义指令开始标记指定的不同不会有问题
       调用时少指定循环变量,则多指定的值不可见
       调用时多指定循环变量,多余的循环变量不会被创建
(2)在模板中定义变量
       在模板中定义的变量有三种类型:
       plain变量:可以在模板的任何地方访问,包括使用include指令插入的模板,使用assign指令创建和替换
       局部变量:在宏定义体中有效,使用local指令创建和替换
       循环变量:只能存在于指令的嵌套内容,由指令(如list)自动创建;宏的参数是局部变量,而不是循环变量
       局部变量隐藏(而不是覆盖)同名的plain变量;循环变量隐藏同名的局部变量和plain变量,下面是一个例子:
<#assign x = "plain">
1. ${x}  <#-- we see the plain var. here -->
<@test/>
6. ${x}  <#-- the value of plain var. was not changed -->
<#list ["loop"] as x>
    7. ${x}  <#-- now the loop var. hides the plain var. -->
    <#assign x = "plain2"> <#-- replace the plain var, hiding does not mater here -->
    8. ${x}  <#-- it still hides the plain var. -->
</#list>
9. ${x}  <#-- the new value of plain var. -->
 
<#macro test>
  2. ${x}  <#-- we still see the plain var. here -->
  <#local x = "local">
  3. ${x}  <#-- now the local var. hides it -->
  <#list ["loop"] as x>
    4. ${x}  <#-- now the loop var. hides the local var. -->
  </#list>
  5. ${x}  <#-- now we see the local var. again -->
</#macro>
输出结果:
1. plain
  2. plain
  3. local
    4. loop
  5. local
6. plain
    7. loop
    8. loop
9. plain2
 
       内部循环变量隐藏同名的外部循环变量,如:
<#list ["loop 1"] as x>
  ${x}
  <#list ["loop 2"] as x>
    ${x}
    <#list ["loop 3"] as x>
      ${x}
    </#list>
    ${x}
  </#list>
  ${x}
</#list>
输出结果:
  loop 1
    loop 2
      loop 3
    loop 2
  loop 1 
       模板中的变量会隐藏(而不是覆盖)数据模型中同名变量,如果需要访问数据模型中的同名变量,使用特殊变量global,下面的例子假设数据模型中的user的值是Big Joe:
<#assign user = "Joe Hider">
${user}          <#-- prints: Joe Hider -->
${.globals.user} <#-- prints: Big Joe -->
(3)名字空间
       通常情况,只使用一个名字空间,称为主名字空间
       为了创建可重用的宏、变换器或其它变量的集合(通常称库),必须使用多名字空间,其目的是防止同名冲突
       创建库
       下面是一个创建库的例子(假设保存在lib/my_test.ftl中):
<#macro copyright date>
  <p>Copyright (C) ${date} Julia Smith. All rights reserved.
  <br>Email: ${mail}</p>
</#macro>
<#assign mail = "jsmith@acme.com"> 
       使用import指令导入库到模板中,Freemarker会为导入的库创建新的名字空间,并可以通过import指令中指定的散列变量访问库中的变量:
<#import "/lib/my_test.ftl" as my>
<#assign mail="fred@acme.com">
<@my.copyright date="1999-2002"/>
${my.mail}
${mail}
输出结果:
  <p>Copyright (C) 1999-2002 Julia Smith. All rights reserved.
  <br>Email: jsmith@acme.com</p>
jsmith@acme.com
fred@acme.com
可以看到例子中使用的两个同名变量并没有冲突,因为它们位于不同的名字空间
       可以使用assign指令在导入的名字空间中创建或替代变量,下面是一个例子:
<#import "/lib/my_test.ftl" as my>
${my.mail}
<#assign mail="jsmith@other.com" in my>
${my.mail}
       输出结果:
jsmith@acme.com
jsmith@other.com
       数据模型中的变量任何地方都可见,也包括不同的名字空间,下面是修改的库:
<#macro copyright date>
  <p>Copyright (C) ${date} ${user}. All rights reserved.</p>
</#macro>
<#assign mail = "${user}@acme.com">
       假设数据模型中的user变量的值是Fred,则下面的代码:
<#import "/lib/my_test.ftl" as my>
<@my.copyright date="1999-2002"/>
${my.mail}
       输出结果:
  <p>Copyright (C) 1999-2002 Fred. All rights reserved.</p>
Fred@acme.com

 
Freemarker - 几个比较实用的例子 - -
                                      用Freemarker做模本语言有一段时间了,列出几个和JSP或者Velocity相比起来比较方便的用途,目的是引诱更多的人跳上Freemarker这个贼船,

1. String内置的JavaScript转换: js_string
用途:用于JavaScript转义,转换',",换行等特殊字符
模板:

<script>
alert("${errorMessage?js_string}");
</script>


输出:

<script>
alert("Readonly\'s pet name is \"Cross Bone\"");
</script>


2.内置的默认值处理:default
用途: 用于处理默认值
模本:

User: ${userLogin.name?default("Anonymous")}

<td>${(employee.department.manager.name)?default(" ")}</td>


输出:

User: Anonymous
<td> </td>


注,可以对整个对象树加上(),再用内置处理器这种方便的做法,偶也是最近刚学会的,以前一直用很傻的方法做.....

3. Sequence内置的计数器: xxx_index
用途:显示序号
模板:

<#list employees as e>
${e_index}. ${e.name}
</#list>


输出:

1. Readonly
2. Robbin


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

<#assign seq = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']>
<#list seq?chunk(4) as row>
<ul>
<li><#list row as cell>${cell} </#list></li>
</ul>
</#list>

<#list seq?chunk(4, '-') as row>
<tr>
<td><#list row as cell>${cell} </#list></td>
</tr>
</#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>
平常在使用的时候,如果需要排序,一般是通过DAO层实现,如果要随时更改排序方式,确实有些不便。不过freemarker已经给我们想到了,下面是使用方法:
<#list list?sort_by("time") as v>


确实简单吧,不过这是升序的排列方式,如果要降序的话,是这样的:
<#list list?sort_by("time")?reverse as v>


一般写法是这个样子的
<#list dataEntity as de>
${de.name}

要是排序,则用
<#list dataEntity?sort as de>
${de.name}




这是按元素的首字母排序。cs.Phontol.com若要按list中对象元素的某一属性排序的话,则用
<#list dataEntity?sort_by([“name”]) as de>
${de.name}



这个是按list中对象元素的[name]属性排序的,是升序,而我们大多情况要用到降排序,这时得改成如下的样子:
<#list dataEntity?sort_by([“name”])?reverse as de>
${de.name}

Map 里的value 和key
分享到:
评论

相关推荐

    机器人路径规划中A*与DWA算法融合的Python实现及应用

    内容概要:本文详细介绍了A*搜索算法和DWA(动态窗口法)算法的基本原理及其在机器人路径规划中的融合应用。A*算法用于全局路径规划,通过启发式搜索找到从起点到目标点的最短路径;DWA算法则专注于局部动态环境下的实时避障,确保机器人在移动过程中能够灵活避开障碍物。两者结合可以在复杂环境中提供高效的路径规划解决方案。文中提供了详细的Python代码实现,帮助读者理解和实践这两种算法的融合方法。 适合人群:对机器人路径规划感兴趣的初学者和技术爱好者,尤其是有一定编程基础的小白。 使用场景及目标:适用于需要在静态和动态环境中进行路径规划的机器人项目。主要目标是使机器人能够在复杂环境中安全、高效地到达目标位置,同时避免障碍物。具体应用场景包括但不限于室内导航、无人驾驶车辆等。 其他说明:文章不仅讲解了理论知识,还给出了具体的代码实现和调试技巧,如启发函数的选择、速度空间采样的优化、评分权重的调整等。此外,还提到了一些常见问题及解决方法,如机器人在遇到突发情况时的行为调整。

    ELk整体介绍整理PPT涵盖了ELk 相干的架构整理

    ELK日志架构部署与应用指南:构建高效、可靠的日志管理与分析系统 本指南全面介绍ELK(Elasticsearch、Logstash、Kibana)日志架构的部署实践与优化策略,旨在帮助技术团队构建可扩展、高容错的日志处理平台。内容涵盖ELK核心组件(Filebeat、Logstash、Kafka、Elasticsearch、Kibana)的工作原理、架构设计、配置案例及最佳实践,重点解析以下关键内容: ● 架构选型与演进:从基础架构到分布式集群,对比单节点、轻量级(Filebeat+Logstash)及引入Kafka的高可用架构优劣,指导根据实际场景选择合适的部署模型。 ● 组件深度解析:详细讲解Filebeat轻量级采集、Logstash灵活处理、Kafka消息缓冲、Elasticsearch分布式索引及Kibana可视化分析的实现机制与性能调优。 ● 日志规范与实战:明确日志打印格式标准(时间、级别、组件、用户信息等),结合配置示例(如Nginx日志采集到Kafka的动态Topic生成)演示数据流转全过程。 ● 运维与扩展:涵盖集群高可用设计、负载均衡策略、日志实时监控及异常报警机制,为系统稳定性与后续扩展提供完整解决方案。 本指南适合日志管理需求的中大型企业技术团队,可作为从架构设计到落地实施的实操手册,助力提升日志分析效率与系统运维能力。

    T型三电平逆变器VSG控制与LCL滤波器的双闭环设计及优化

    内容概要:本文详细介绍了基于T型三电平逆变器的虚拟同步机(VSG)控制技术,涵盖VSG的核心算法、中点电位平衡策略以及LCL滤波器的双闭环控制设计。首先探讨了VSG控制的基本原理,包括虚拟惯量和阻尼特性的模拟,以及有功-频率和无功-电压下垂控制的具体实现。针对T型三电平拓扑特有的中点电位漂移问题,提出了多种平衡控制方法。对于LCL滤波器,讨论了其参数设计和双闭环控制策略,特别是电流环PI参数的选择和避免谐振的方法。文中还提供了多个实用的经验公式和调试技巧,并引用了相关领域的权威文献作为理论支持。 适合人群:从事电力电子、新能源并网系统研究和开发的技术人员,尤其是有一定电力电子基础的研发人员。 使用场景及目标:适用于需要深入了解和掌握VSG控制技术和LCL滤波器设计的研究人员和技术开发者。主要目标是帮助读者理解和实现T型三电平逆变器的VSG控制,提高系统的稳定性和性能。 其他说明:文中不仅提供了详细的理论解释,还有具体的代码实现和调试建议,便于读者进行实际操作和验证。同时强调了调试过程中需要注意的安全事项和常见问题的解决方案。

    Go语言Go语言简介:特性、应用场景与学习资源汇总:服务器端开发、云计算、微服务架构的最佳选择

    内容概要:本文介绍了Go语言(又称Golang)的特点、应用场景和发展背景。Go语言由Google团队于2007年设计,2009年发布,以其简洁的语法、高效的编译性能和强大的并发支持著称。它摒弃了复杂的面向对象特性,采用接口和组合的方式实现代码复用。作为编译型语言,Go语言通过严格的类型检查确保代码质量,并内置了高效的垃圾回收机制。Go语言广泛应用于服务器端开发、云计算和微服务架构等领域,如Google、Docker和Uber等公司均采用Go语言构建后端服务。此外,Go语言拥有丰富的学习资源,包括官方文档、社区支持以及权威书籍和在线课程。; 适合人群:对编程有一定了解,特别是对高性能后端开发感兴趣的开发者。; 使用场景及目标:①希望掌握一门适用于服务器端开发、云计算和微服务架构的编程语言;②想深入了解并发编程和支持高并发场景的开发技术。; 其他说明:学习Go语言不仅可以提升编程技能,还能借助其活跃的社区和丰富的学习资源,快速适应现代互联网应用开发的需求。

    Ollama0.6.2安装文件-2

    Ollama0.6.2安装文件-2

    【Java并发编程】线程同步工具与锁机制详解:CountDownLatch、CyclicBarrier、Semaphore的应用场景及实现方式并发编程中的多个

    内容概要:本文详细介绍了Java并发编程中的多个重要概念和技术,包括CountDownLatch、CyclicBarrier、Semaphore等同步工具类,深入探讨了线程的创建方式、运行状态及线程安全问题。文章还讲解了原子性、可见性、有序性三大特性及其解决方案,如锁机制和原子类的应用。此外,文中对比了悲观锁和乐观锁的特点与适用场景,并阐述了线程池的核心参数配置。最后,简要介绍了Spring框架的核心技术,如IOC、AOP及事务管理的基本概念。 适合人群:具备一定Java编程基础,尤其是对并发编程和Spring框架有一定了解的研发人员。 使用场景及目标:①帮助开发者理解Java并发编程中的同步工具类、线程管理及线程安全问题;②指导开发者在实际项目中选择合适的锁机制和线程池配置;③加深对Spring框架中IOC、AOP及事务管理的理解,提升开发效率。 其他说明:本文不仅提供了理论知识,还结合了代码示例,便于读者理解和实践。建议读者在学习过程中结合实际项目需求进行调试和验证,以更好地掌握相关技术。

    【系统编程语言】Rust语言特性与应用:现代高效安全的系统级开发工具介绍

    内容概要:Rust是一种由Mozilla研究院开发的现代系统编程语言,专注于安全、并发和性能。其设计哲学是“零成本抽象”,即在不影响性能的前提下提供高级抽象。Rust通过所有权系统确保内存安全,避免了垃圾回收的需求,有效防止了悬垂指针、数据竞争和无效内存访问等问题。它提供了强大的并发原语,如线程、通道和原子操作,使并发编程更加简单和安全。此外,Rust编译为机器码,性能接近C/C++,并且支持多种操作系统和架构,包括Windows、Linux、macOS以及嵌入式系统。Rust还拥有活跃的社区和丰富的生态系统,提供了大量涵盖多个领域的库和工具。Rust适用于高性能和安全性的系统级应用,如操作系统、数据库、网络服务器和嵌入式系统,同时也被广泛应用于WebAssembly、区块链和人工智能等领域。; 适合人群:对系统编程感兴趣,尤其是希望在保证性能的同时提升代码安全性的开发者。; 使用场景及目标:①需要开发高性能、安全的系统级应用,如操作系统、数据库、网络服务器等;②希望深入理解内存管理和并发编程的最佳实践;③探索WebAssembly、区块链和人工智能等新兴技术领域。; 阅读建议:Rust不仅适合有经验的系统程序员,也适合希望通过学习现代编程语言提升技能的新手。官方教程《The Rust Programming Language》、实践指南《Rust by Example》和练习项目《Rustlings》都是很好的学习资源,建议结合这些资源进行实践和调试。

    基于一致性算法的直流微电网均流均压二级控制方案及其应用

    内容概要:本文介绍了一种基于一致性算法的直流微电网均流均压二级控制方案。该方案采用分布式二级控制器,通过邻居间的通信来计算控制动作,从而实现高效稳定的均流和均压控制。文中详细讨论了恒功率负载平衡点的存在条件,并介绍了即插即用特性的实现。此外,通过MATLAB/Simulink仿真验证了该方案的电压稳定性和鲁棒性。 适合人群:从事电力电子、微电网控制领域的研究人员和技术人员,尤其是对分布式控制系统感兴趣的读者。 使用场景及目标:适用于直流微电网的设计与优化,旨在提高系统的稳定性和效率,特别是在应对非线性负载和突发扰动的情况下。 其他说明:该方案在理论和实践中展现了显著的优势,如更高的均流精度和更快的动态响应时间。然而,仿真运行时间较长,需考虑计算资源的分配。

    电路仿真:基本电路元件仿真.zip

    电子仿真教程,从基础到精通,每个压缩包15篇教程,每篇教程5000字以上。

    前端开发基于200道高频面试题的JavaScript核心技术解析:互联网大厂面试指南了互联网大厂

    内容概要:本文档《互联网大厂200道高频Javascript面试题.pdf》涵盖了广泛的JavaScript知识点,针对200道高频面试题进行了详细解析。内容涉及JavaScript的核心概念(如闭包、原型链、事件循环)、异步编程(如Promise、async/await)、数据类型(如BigInt、Symbol)、DOM操作(如EventTarget、MutationObserver)、性能优化(如防抖、节流)、以及最新的ES提案特性(如import.meta、top-level await)。每个问题不仅提供了简明的答案,还深入探讨了背后的原理和应用场景,帮助读者全面掌握JavaScript的各种特性和最佳实践。 适合人群:具备一定编程基础,特别是对JavaScript有初步了解的前端开发人员,以及准备面试互联网大厂的求职者。 使用场景及目标:①巩固JavaScript基础知识,理解语言内部机制;②掌握常见面试题及其解答技巧,为技术面试做准备;③通过实际案例和代码示例加深对JavaScript特性的理解,提高编程能力。 阅读建议:此资源内容详实,建议读者根据自身水平选择重点章节进行学习,同时结合实际项目或练习题进行实践,以达到更好的学习效果。对于复杂的概念,可以多次阅读并查阅相关资料,确保彻底理解。

    【C#编程语言】从入门到精通:C#核心概念、开发工具与实战项目解析

    内容概要:本文详细介绍了C#编程语言的基础知识和学习路径。首先阐述了C#的背景和优势,强调其广泛的应用领域和良好的就业前景。接着指导读者如何准备学习环境,包括安装Visual Studio等开发工具。文中深入讲解了C#的基础语法,如数据类型、运算符、流程控制语句等,并探讨了面向对象编程的核心概念,包括类与对象、封装、继承、多态等。此外,还介绍了异常处理机制,并通过一个控制台版的学生管理系统项目,帮助读者将理论应用于实践。最后,推荐了一系列学习资源,鼓励读者持续学习和探索C#的高级特性。 适合人群:对编程有兴趣的初学者,尤其是希望从事软件开发或提升编程技能的人士。 使用场景及目标:①帮助读者从零开始系统学习C#,掌握基本语法和核心概念;②通过实战项目巩固所学知识,培养解决实际问题的能力;③为后续深入学习C#的高级特性和应用领域打下坚实基础。 其他说明:本文不仅提供了详尽的知识点讲解,还注重实践操作和项目经验积累,适合自学或作为培训教材使用。建议读者跟随文章步骤逐步实践,并利用推荐的学习资源深化理解。

    油气开采中液氮压裂的COMSOL热-流-固-损伤耦合模型研究

    内容概要:本文详细介绍了使用COMSOL软件建立液氮压裂的热-流-固-损伤耦合模型的方法和技术细节。液氮压裂作为一种环保高效的油气开采方法,能够利用低温特性减少地层污染并提高裂缝扩展效率。文中探讨了模型的关键组件,如传热、达西流、固体力学以及自定义的损伤演化方程,并展示了如何将这些元素整合在一个统一的多物理场环境中进行仿真。此外,还讨论了网格划分、求解器设置、边界条件处理等方面的具体实现方法,以及如何通过后处理手段来分析和展示仿真结果。 适合人群:从事油气田开发、地质工程、计算力学等相关领域的科研人员和技术专家。 使用场景及目标:适用于需要深入理解液氮压裂过程中复杂的物理机制的研究项目,旨在为优化压裂工艺提供理论支持和技术指导。 其他说明:文中提供了大量具体的数学公式和代码片段,帮助读者更好地理解和重现所描述的技术流程。同时强调了不同参数选择对于最终仿真结果的影响,提醒使用者注意实际应用中的各种挑战。

    ### 【企业AI应用】从DeepSeek到Manus:AI重塑企业价值与应用实践(2025年华中科技大学研究报告)

    内容概要:本文探讨了AI如何重塑企业价值,特别是通过DeepSeek和Manus这两个技术的推动。DeepSeek以其低成本、高质量的特性,打破了AI应用的技术和成本壁垒,实现了AI赋能平权。Manus作为通用智能体,通过全链路自主执行和多智能体协同架构,显著提升了企业效率并降低了成本。文章详细介绍了AI在多个行业(如金融分析、人力资源、零售运营等)的具体应用案例,展示了AI如何通过数据驱动、自动化和智能化手段,帮助企业实现降本增效、商业模式创新和产品迭代。此外,文中还提出了企业在拥抱AI过程中面临的挑战和应对策略,强调了数据基础、技术选择和组织能力建设的重要性。 适合人群:企业高管、AI技术从业者、数字化转型顾问及对AI感兴趣的创业者。 使用场景及目标:①了解AI技术在企业中的应用场景及其带来的变革;②为企业制定AI战略和实施路径提供参考;③帮助企业识别潜在的AI赋能机会,优化业务流程,提升竞争力。 其他说明:文章通过大量实际案例和数据支持,强调了AI技术在企业中的巨大潜力和现实可行性。同时,提醒企业在拥抱AI时需谨慎规划,避免盲目跟风,确保AI项目的成功落地。

    c语言学习资料,共208页

    c语言学习资料,共208页

    无人车路径规划中基于人工势场算法的MATLAB实现及优化

    内容概要:本文详细探讨了基于人工势场的无人车避障路径规划算法。主要内容包括三个主要势场的建立及其MATLAB代码实现:引力势场用于吸引无人车向目标点移动;障碍车斥力势场用于使无人车避开障碍物;道路边界势场确保无人车保持在车道内行驶。此外,文中还介绍了如何通过调整不同势场的增益系数、引入朝向因子和动量项等手段优化路径规划性能,提高路径平滑性和安全性。最终形成了完整的路径规划流程,即计算合力、确定方向并迭代推进,使得无人车能够在复杂环境中顺利导航。 适合人群:对无人驾驶技术和路径规划算法感兴趣的科研人员、工程师及高校相关专业学生。 使用场景及目标:适用于模拟和实际测试无人车在静态或动态环境中的避障能力和路径选择行为,旨在提高无人车的安全性和智能水平。 其他说明:文中提供的MATLAB代码仅为简化版本,实际应用中可根据具体需求进一步调整和完善。

    电力系统中分布式电源接入对电网电压影响的潮流计算研究

    内容概要:本文详细探讨了分布式电源(如光伏、风能、燃料电池等)接入电网时对电压产生的影响。通过具体的潮流计算模型,展示了不同类型的分布式电源(DG)在接入电网时所采用的不同节点类型(PQ节点、PV节点等),并分析了它们对电压稳定性的影响。文中还提供了基于Python的Pandapower库构建的测试电网实例,以及MATLAB中的节点类型动态切换逻辑,进一步解释了不同节点类型在实际应用中的表现及其优缺点。 适合人群:从事电力系统研究、分布式能源规划的技术人员和研究人员。 使用场景及目标:帮助技术人员理解分布式电源接入电网时的电压波动机制,优化分布式电源的接入方式,提高电网电压稳定性和可靠性。 其他说明:文章强调了在进行电网规划时,应根据实际情况选择合适的节点类型,避免盲目依赖理想化模型。同时,提出了混合模式作为一种有效的解决方案,能够在不同工况下保持较好的电压质量。

    三星轮爬楼车sw18可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    三星轮爬楼车sw18可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    电子封装材料仿真:复合材料仿真.zip

    电子仿真教程,从基础到精通,每个压缩包15篇教程,每篇教程5000字以上。

    户外储能电源双向逆变器板生产资料及技术规格详解

    内容概要:本文详细介绍了户外储能电源双向逆变器板的技术资料及其特点。涵盖原理文件、PCB文件、源代码、电感与变压器规格参数等,适用于2KW(最大3KW)的户外储能电源。文中强调了双向软开关DC-DC设计、两颗M0+ 32位MCU的分工、SPWM调制方式、H桥IGBT的应用、详细的电气参数和技术特性。此外,还包括了SPWM信号生成代码示例、硬件设计细节、生产注意事项等。 适合人群:从事户外储能电源开发的技术人员、电子工程师、产品经理等。 使用场景及目标:帮助开发者快速掌握双向逆变器板的设计和生产要点,缩短产品研发周期,提高产品质量和可靠性。具体应用场景包括但不限于户外应急电源、便携式储能设备等。 其他说明:本文提供了丰富的技术细节和实践经验,如双向软开关DC-DC设计、SPWM调制、IGBT驱动、EMC整改记录等,有助于解决实际开发中的难题。同时,附带的实际案例展示了该方案的成功应用,进一步证明了其可行性和优越性。

    RSA算法生成软件签名

    可用于生成RSA密钥对及验证签名

Global site tag (gtag.js) - Google Analytics