现在的位置: 首页 > 综合 > 正文

FreeMarker学习笔记(一) 初步认识Freemarker的模板

2018年03月29日 ⁄ 综合 ⁄ 共 11590字 ⁄ 字号 评论关闭

 *******************************************************

*                   转载请注明来源地址http://blog.csdn.net/csuliky   作者:阿布

*******************************************************

首先我们来对Freemarker有一个初略的认识,在这一章中,我们要学会如何写出一个简单但是非常有效的Freemarker模板。

1.模板+数据模型=输出

假设你的一个电子商铺应用程序需要一个HTML页面,类似于这个:

view plaincopy to clipboardprint?
<html> 
<head> 
  <title>Welcome!</title> 
</head> 
<body> 
  <h1>Welcome Big Joe!</h1> 
  <p>Our latest product:  
  <a href="products/greenmouse.html" mce_href="products/greenmouse.html">green mouse</a>!  
</body> 
</html>   
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome Big Joe!</h1>
  <p>Our latest product:
  <a href="products/greenmouse.html" mce_href="products/greenmouse.html">green mouse</a>!
</body>
</html>  

我们说用户名("Big Joe")应该依赖于访问这个web页面的登录者,最新的产品应该来自于数据库,而且随时都会更新,在这种情况下,你不可能输入用户名、URL或者最新的产品在这个HTML页面里,因为你不可能使用静态的HTML页面。

FreeMarker对这个问题的解决方案是使用模板(template)来替代静态的HTML页面。模板除了使用FreeMarker的命令,使之能动态化以外,其他的和静态的HTML页面相同:

view plaincopy to clipboardprint?
<html> 
<head> 
  <title>Welcome!</title> 
</head> 
<body> 
  <h1>Welcome ${user}!</h1> 
  <p>Our latest product:  
  <a href="${latestProduct.url}" mce_href="${latestProduct.url}">${latestProduct.name}</a>!  
</body> 
</html>   
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${user}!</h1>
  <p>Our latest product:
  <a href="${latestProduct.url}" mce_href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>  

模板存储在Web服务器,通常就像静态HTML页面。但是无论何时访问这个页面,FreeMarker都会及时的通过把${...}替换成为最新的内容,来将模板转换为普通的HTML页面(例如:把${user}替换为"Big Joe"或者是任何访问该页面的用户)并将结果发送到访问者的浏览器上。这样访问者的浏览器将收到像第一个例子中的HTML页面(例如,普通的不带FreeMarker说明的HTML页面),并且用户不会感觉到其实服务器是在使用FreeMarker。在此期间,模板还在服务器上,没有任何改变,当有人再次访问这个页面时,FreeMarker将再次执行,以保证页面显示的信息总是最新的。现在你可能已经注意到模板没有包含任何命令来发现当前的访问者是谁,或者连接到数据库去查询以得到最新的产品。它看起来好像早已知道这些值。而且确实是这种情况。FreeMarker有一个重要的思想(其实是在MVC)就是显示逻辑和业务逻辑应该分离。在模板中你只要处理视觉问题,也就是说,视觉设计的问题,格式的问题。显示的数据(例如:用户名等等)是在FreeMarker之外来准备的。通常是Java语言或者其他普通的目标语言。这样模板的创建者不需要知道数据是怎样计算的。其实,在模板不作任何修改的情况下,数据完全可能改变,同时页面的外观也可能发生改变。当模板设计者和程序设计者是分开的时候,这种分离会特别有用。FreeMarker(模板设计者)对于数据的计算并不没有兴趣,但是FreeMarker必须知道实际的数据是什么。所有模板要使用的数据将被打包在一个数据模型中。数据是由刚才提到的程序设计者来创建的。模板设计者关心的是数据要像树型结构(就像你硬盘中的目录和文件一样),如下所示:

view plaincopy to clipboardprint?
(root)  
  |  
  +- user = "Big Joe" 
  |  
  +- latestProduct  
      |  
      +- url = "products/greenmouse.html" 
      |  
      +- name = "green mouse"   
(root)
  |
  +- user = "Big Joe"
  |
  +- latestProduct
      |
      +- url = "products/greenmouse.html"
      |
      +- name = "green mouse"  

将这个与你早先看到的模板中的${user}和${lastestProduct.name}比较。就可以推断出数据模型就像计算机系统:一个根路径和一个lastestProduct目录,而url与那么就是lastestProduct目录下的文件。

总结:FreeMarker需要模板和数据模型来生成输出文件(通常是HTML文件)。

2.数据模型一览

正如你所看到的,数据模型基本上是一棵树。这棵树可以非常复杂和深,例如:

view plaincopy to clipboardprint?
(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"     
    
    
(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"  
 
  
 

当作目录的变量被称为哈希表。哈希表存储其他变量的查询名称(例如:"animals","mouse"或者"price")。存储单个值的变量称为标量。当你想使用模板中的一个子变量的时候,你要从根开始指定它的路径,并且每一级用"."分隔开。为了访问price的mouse,你要从根开始,进入animals,然后进入mouse,最后进入price。因此你要写上"animals.mouse.price"。当你将${...}像这样置于表达式周围的时候,你是在告诉FreeMarker在这个点上输出相应的文字。还有另外一类重要的变量:sequences(序列)。它们类似于哈希表,但是它们不能存储它们所包含的变量的名字。相反,它们按顺序存储子变量,你可以用一个数字索引来访问。例如:在这个数据模型中,animals和whatnot.fruits就是sequences:

view plaincopy to clipboardprint?
(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"     
    
    
(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"  
 
  
 

为了访问一个序列的子变量,你可以在方括号中使用数字索引。索引从0开始,因而索引的第一个项就是0,而第二项就是1,以此类推。所以为了访问第一个animal,你要写上"animals[0].name",为了访问"whatnot.fruits"的第二项,就要写上"whatnot.fruits[1]"。标量可以进一步分成这些分类:

String: 文本,就是任意的字符序列,例如:像上面的"m","o","u","s","e"。譬如:name、size就是string。

Number: 就是数字的值,像上面的price。注意"50"和50在FreeMarker中是完全不一样的。 前者是一个字符串,而后者是数字。

Date/time: 时间或日期。

Boolean: 值为:true/false (也可以是yes/no, 或者on/off)。 像上面的"protected"子变量就表示动物是否受保护。

总结:

数据模型形象的说就是一棵树。

标量存储单个值。可以是字符串、数字、日期/时间、或者是一个布尔值。

哈希表是存储其他的变量的容器,并且这些变量与它们的一个可查找的名字相关联。

序列就是按顺序存储其他变量的容器,可以通过数字索引来查找其中的值。数字索引从0开始。

3.模板一览

最简单的模板就是HTML文件(或者纯文本文件--FreeMarker并不限于HTML)。当客户端访问那个页面时,FreeMarker将那个HTML页面原样发送至客户端。然而如果你想让页面具有更多的动态性,那么你要开始将FreeMarker可以解析的特殊的部分添加到HTML页面中:

${... } : FreeMarker将在输出时使用实际值替换掉花括号中的东西。这称之为插值(interpolation )。

FTL tags (FreeMarker Template Language tags): FTL(FreeMarker模板标记语言)有点类似于HTML标记, 但是FTL是FreeMarker指令,并且不会在输出时打印出来。FTL标记以"#"开头。(用户自定义的FTL标记用"@"代替"#",但这是高级话题中的部分)

Comments: FreeMarker注释类似于HTML注释,但是由<#--和-->限定。在定界符之间包括定界符在内的内容都将被FreeMarker忽略,并且不会再输出时写入。

任何非FTL标记、者插值或者是注释在FreeMarker中都将被视为静态文本,也不会被FreeMarker解释,而只是在输出时原样输出。FTL标签你所说的指令与HTML标签的关系和HTML标签与HTML元素的关系(例如:<table>元素)是一样的。(如果你不不能区别FTL标记与FreeMarker指令的话,就把它们当作同义词吧。)

 指令示例
虽然FreeMarker有许多指令,但是在概述中,我们仅介绍常用的三个。

 if指令
使用if指令,你可以有条件的跳过模板中的一部分,假设在下列例子中你想问候你的老板,Big Joe,以有别于其他用户:

view plaincopy to clipboardprint?
<html> 
<head> 
  <title>Welcome!</title> 
</head> 
<body> 
  <h1> 
    Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!  
  </h1> 
  <p>Our latest product:  
  <a href="${latestProduct.url}" mce_href="${latestProduct.url}">${latestProduct.name}</a>!  
</body> 
</html>   
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>
    Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!
  </h1>
  <p>Our latest product:
  <a href="${latestProduct.url}" mce_href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>  

这里你告诉FreeMarker"当user值为"Big Joe"的时侯, 加上", our beloved leader" 。通常在if条件为false时,<#if 条件>和</#if>之间的内容会被跳过。让我们来详细说明一下这里使用的条件:"=="是一个操作符用来测试它左边的值和右边的值是否相等,并且据此返回一个值为true或者是false。在"=="的左边,我引用一个变量的语法,应该已经非常熟悉,将用变量的值来取代。一般情况下, 在指令或插值中引起来的词就是提到的变量。右边我已经指定了一个字符串。模板中的字符串必须始终放在引号内。下面将打印"Pythons are free today!",加入他们的价格为0:

view plaincopy to clipboardprint?
<#if animals.python.price == 0> 
  Pythons are free today!  
</#if>  
<#if animals.python.price == 0>
  Pythons are free today!
</#if> 

稍早时作为一个字符串直接指定,这里直接指定为( 0 ) 。请注意,数字不能被引起来。如果你用引号引起它( "0" ) , 那么将被FreeMarker曲解为字符串:

view plaincopy to clipboardprint?
<#if animals.python.price != 0> 
  Pythons are not free today!  
</#if>   
<#if animals.python.price != 0>
  Pythons are not free today!
</#if>  

正如你所猜的那样,"!=" 是不等于的意思。你也可以这样写:

view plaincopy to clipboardprint?
<#if animals.python.price < animals.elephant.price> 
  Pythons are cheaper than elephants today.  
</#if>   
<#if animals.python.price < animals.elephant.price>
  Pythons are cheaper than elephants today.
</#if>  

使用<#else> 标签,你可以指定如果if条件为false,将怎样处理。例如:

view plaincopy to clipboardprint?
<#if animals.python.price < animals.elephant.price> 
  Pythons are cheaper than elephants today.  
<#else> 
  Pythons are not cheaper than elephants today.  
</#if>   
<#if animals.python.price < animals.elephant.price>
  Pythons are cheaper than elephants today.
<#else>
  Pythons are not cheaper than elephants today.
</#if>  

这将打印''Pythons are cheaper than elephants today.'',加入巨蟒的价格低于大象的价格,或者else打印出''Pythons are not cheaper than elephants today.''。如果你有一个布尔值(true/false)的变量,那么你可以在if条件中直接使用:

view plaincopy to clipboardprint?
<#if animals.python.protected> 
  Warning! Pythons are protected animals!  
</#if>   
<#if animals.python.protected>
  Warning! Pythons are protected animals!
</#if>  

 list指令
当以想要列出某些东西的时候,列表指令是很后用的。例如如果你要是想先前的一个示例(data-model I used earlier to demonstrate sequences )来合并模板:

view plaincopy to clipboardprint?
<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
  <#list animals as being>
  <tr><td>${being.name}<td>${being.price} Euros
  </#list>
</table>  

输出将会是这样:

view plaincopy to clipboardprint?
<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>   
<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>  

通常list指令的格式如下:

<#list 作为循环变量的序列 >重复部分 </#list>

重复部分 会在你所提供的序列的每一个项被重复,从第一个项开始,一个接着一个。在所有的重复中,循环变量会保存当前项的值。这个变量仅在<#list ...> 和</#list> 标签中存在。作为另一个示例,我们列出数据模型中的水果:

view plaincopy to clipboardprint?
<p>And BTW we have these fruits:  
<ul> 
<#list whatnot.fruits as fruit> 
 <li>${fruit}  
</#list> 
<ul>   
<p>And BTW we have these fruits:
<ul>
<#list whatnot.fruits as fruit>
 <li>${fruit}
</#list>
<ul>  

whatnot.fruits 表达式对你来说应该熟悉了吧。

 include指令
使用include 指令你可以在模板中插入另外一个文件。假设你必须要在许多页面中显示同样的版权标志,你可以创建一个包含版权标志的文件,并在你需要显示的地方插入。比方说,你将版权标志保存在copyright_footer.html :

view plaincopy to clipboardprint?
<hr> 
<i> 
Copyright (c) 2000 <a href="http://www.acmee.com" mce_href="http://www.acmee.com">Acmee Inc</a>,  
<br> 
All Rights Reserved.  
</i>   
<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com" mce_href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>  

任何你需要的时候,都可以简单的用include 指令来插入:

view plaincopy to clipboardprint?
<html> 
<head> 
  <title>Test page</title> 
</head> 
<body> 
  <h1>Test page</h1> 
  <p>Blah blah...  
<#include "/copyright_footer.html"> 
</body> 
</html>   
<html>
<head>
  <title>Test page</title>
</head>
<body>
  <h1>Test page</h1>
  <p>Blah blah...
<#include "/copyright_footer.html">
</body>
</html>  

并且输出如下:

view plaincopy to clipboardprint?
<html> 
<head> 
  <title>Test page</title> 
</head> 
<body> 
  <h1>Test page</h1> 
  <p>Blah blah...  
<hr> 
<i> 
Copyright (c) 2000 <a href="http://www.acmee.com" mce_href="http://www.acmee.com">Acmee Inc</a>,  
<br> 
All Rights Reserved.  
</i> 
</body> 
</html>   
<html>
<head>
  <title>Test page</title>
</head>
<body>
  <h1>Test page</h1>
  <p>Blah blah...
<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com" mce_href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>
</body>
</html>  

如果你改变了copyright_footer.html 文件, 那么访问者会在所有的页面看都新的版权通知。

综合使用指令
你可以在页面上多次使用指令,并且可以像嵌套HTML元素那样来嵌套使用FreeMarker的指令。例如这将列出所有的动物并使用大字号打印动物的名字:

view plaincopy to clipboardprint?
<p>We have these animals:  
<table border=1> 
  <tr><th>Name<th>Price  
  <#list animals as being> 
  <tr> 
    <td> 
      <#if being.size == "large"><font size="+1"></#if> 
      ${being.name}  
      <#if being.size == "large"></font></#if> 
    <td>${being.price} Euros  
  </#list> 
</table>   
<p>We have these animals:
<table border=1>
  <tr><th>Name<th>Price
  <#list animals as being>
  <tr>
    <td>
      <#if being.size == "large"><font size="+1"></#if>
      ${being.name}
      <#if being.size == "large"></font></#if>
    <td>${being.price} Euros
  </#list>
</table>  

注意由于FreeMarker并不解释外部的FTL标签、插值和注释,所以它明白上述font标签嵌套的严重性。

 处理找不到的变量
实际上数据模型中经常有些参数是可选的。 发现人们犯了典型的错误, 如果变量找不到,除非你明确告诉FreeMarker怎样处理,否则它不会容许参考的变量变量丢失。在这里,我们将展示两个最典型的方式例子。

注意对于程序员:一个不存在的变量和一个为null的变量对于FreeMarker是同一回事,所以"找不到"涵盖了上述两种情况。

任何你提到变量的地方,你都可以使用"!"和缺省值,来在变量找不到时指定一个缺省值。像下面这个例子,在数据模型中当user 找不到时, 模板会假设user 的值就是"Anonymous" :

     
  <h1>Welcome ${user!"Anonymous"

}!</h1> 

 
  
     

是不是在变量的名字后加上"??"就表示找不到呢。 结合这个和已经介绍的if 指令你可以跳过整个问候,如果user 变量找不到的话:

<#if user??

><h1>Welcome ${user}!</h1></#if> 

 

抱歉!评论已关闭.