您的位置:首页 > 其它

FreeMarker --- 网页静态化入门简介

2018-02-02 10:59 357 查看
一、概述
FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写。FreeMarker被设计用来生成HTMLWeb页面,特别是基于MVC模式的应用程序。
所谓模板,就是一份已经写好了基本内容,有着固定格式的文档,其中空出或者用占位符标识的内容,由使用者来填充,不同的使用者给出的数据是不同的。在模板中的占位符,在模板运行时,由模板引擎来解析模板,并采用动态数据替换占位符部分的内容。
FreeMarker不是一个Web应用框架,而适合作为Web应用框架一个组件,FreeMarker与Web容器无关,即在Web运行时,它并不知道Servlet或HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成XML,JSP或Java文件等。
虽然FreeMarker具有一些编程的能力,但通常由Java程序准备要显示的数据,由FreeMarker生成页面,通过模板显示准备的数据(如下图)



分离表现层和业务逻辑
使用JSP开发过程中在页面中大量的存在业务逻辑的代码,使得页面内容凌乱,在后期大量的修改维护过程中就变得非常困难。FreeMarker根本不支持Java脚本代码,而是使用el表达式来输出展示数据。FreeMarker的设计初衷就是:模板+数据模型=输出,模板只负责数据在页面中的表现,不涉及任何的逻辑代码,而所有的逻辑都是由数据模型来处理的。用户最终看到的输出是模板和数据模型合并后创建的。
 
 
提高开发效率
在我们以往的开发中,使用的都是JSP页面来展示数据的,即所谓的表现层。我们都知道,JSP在第一次执行的时候需要转换成Servlet类,开发阶段进行功能调适时,需要频繁的修改JSP,每次修改都要编译和转换,那么试想一天中我们浪费在程序编译的时间有多少。相对于JSP来说,FreeMarker模板技术不存在编译和转换的问题,所以就不会存在上述问题。而且开发过程中,我们在不必在等待界面设计开发人员完成页面原形后,我们再来开发程序。
 
 
分工明确
以往用JSP展现数据时,程序员并不熟悉界面设计技术,反之界面开发人员,也并不熟悉程序语言。协调工作很困难,使用FreeMarker后,作为界面开发人员,只专心创建HTML文件、图像以及Web页面的其他可视化方面,不用理会数据;而程序开发人员则专注于系统实现,负责为页面准备要显示的数据。

下载地址:
https://freemarker.apache.org/freemarkerdownload.html

资源:
API:
https://freemarker.apache.org/docs/api/index.html
http://tool.oschina.net/apidocs/apidoc?api=freemarker

用户手册:
https://freemarker.apache.org/docs/index.html

http://freemarker.foofun.cn/

二、入门案例

2.1 在WEB-INF下创建freemarker引擎模板ftl
<link rel="stylesheet" type="text/css" href="css/promotion_detail.css">
<div class="container promotions" >
<div class="col-md-2 prolist">
<h5 class="title"><a href="#/promotion"><strong>返回促销列表</strong></a></h5>
<img src="images/pro.jpg" class="img-responsive">
</div>
<div class="col-md-10 procontent">
<h5 class="title">${promotion.title}</h5>
<div class="intro">
<p>活动范围: ${promotion.activeScope}</p>
<p>活动时间: ${promotion.startDate?string("yyyy-MM-dd")} - ${promotion.endDate?string("yyyy-MM-dd")}</p>
</div>
<div class="partline clearfix"></div>
<div class="promotionbox">
${promotion.description}
</div>
</div>
</div>

2.2 Action中freemarker实现
@Action(value = "promotion_showDetail")
public String showDetail() throws IOException, TemplateException {
// 先判断id对应html是否存在,如果存在,直接返回
String htmlRealPath = ServletActionContext.getServletContext().getRealPath("/freemarker");
File htmlFile = new File(htmlRealPath + "/" + model.getId() + ".html");

// 如果html文件不存在,查询数据库,结合freemarker模板生成页面
if (!htmlFile.exists()){
// 配置对象,配置模板位置
Configuration configuration = new Configuration(Configuration.VERSION_2_3_22);
configuration.setDirectoryForTemplateLoading(new File(ServletActionContext.getServletContext().getRealPath("/WEB-INF/freemarker_templates")));

// 获取模板对象
Template template = configuration.getTemplate("promotion_detail.ftl","utf-8");

// 动态数据对象
Promotion promotion = WebClient.create(Constant.BOS_MANAGEMENT_URL + "/bos_management/services/promotionService/promotion/" + model.getId()).accept(MediaType.APPLICATION_JSON).get(Promotion.class);
Map<String,Object> parameterMap = new HashMap<>();
parameterMap.put("promotion",promotion);

// 合并输出
template.process(parameterMap,new OutputStreamWriter(new FileOutputStream(htmlFile), "utf-8"));

}

// 如果html存在,直接将响应输出流返回
ServletActionContext.getResponse().setContentType("text/html;charset=utf-8");
FileUtils.copyFile(htmlFile,ServletActionContext.getResponse().getOutputStream());

return NONE;
}

三、FreeMarker模板文件.ftl主要有4个部分组成

3.1 文本:直接输出的部分
3.2 注释:即<#-- ... -->格式部分,不会输出
3.3 插值:即${...}或者#{...}格式的部分,将使用数据模型中的部分替代输出
3.4 FTL指令:FreeMarker指令,和HTML标记类似,名字前加#,例如<#list>

FTL指令规则:FreeMarker有三种FTL标签,这和HTML的标签是完全类似的

开始标签:<#directivename parameters>
结束标签:</#directivename>
空标签: <#directivename parameters />
实际上,使用标签时前面的#符号也可能变成@,如果该指令是一个用户指令而不是系统内建指令时,应将#符号改为@符号

插值规则:FreeMarker的插值有如下两种类型

通用插值:${expr}
数字格式化插值:#{expr}或者#{expr;format}

    
通用插值,有可以分为四种情况
a、插值结果为字符串值:直接输出表达式结果
b、插值结果为数字值:根据默认格式(#setting 指令设置)将表达式结果转换成文本输出。可以使用内建的字符串函数格式单个插值,例如
<#setting number_format = "currency" />
<#assign price = 42 />
${price}
${price?string}
${price?string.number}
${price?string.currency}
${price?string.percent}
    
c、输出值为日期值:根据默认格式(由 #setting 指令设置)将表达式结果转换成文本输出,可以使用内建的字符串函数格式化单个插值,例如
<#assign lastUpdated = "2009-01-07 15:05"?datetime("yyyy-MM-dd HH:mm") />
${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')'")};
${lastUpdated?string.short};
${lastUpdated?string.long};
${lastUpdated?String.full};
   
d、插值结果为布尔值
<#assign foo=true />
${foo?string("是foo","非foo")}

数字格式化插值:数字格式化插值可采用#{expr;format}的形式来格式化数字,其中format可以是:

mX:小数部分最小X位
MX:小数部分最大X位

例如:
<#assign x = 2.582 />
<#assign y =4 />
#{x;M2};
#{y;M2};
#{x;m1};
#{y;m1};
#{x;m1M2};
#{y:m1M2};

四、FreeMarker标签

4.1 assign 
assign指令在前面已经使用了多次,它用于为该模板页面创建或替换一个顶层变量,assign指令的用法有多种,包含
创建或替换一个顶层变量,或者创建或替换多个变量等。

示范代码:
<#assign x>
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as n>
${n}
</#list>
</#assign>

4.2 break 
<#break>指令用于跳出迭代

示例代码:
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x>
${x_index + 1}.${x}<#if x_has_next>,</if>
<#if x="星期四"><#break></#if>
</#list>

4.3 case 
switch , case , default , break这些指令显然是分支指令,作用类似于Java的switch语句

示例代码:
<#switch x>
<#case x = 1>
     1
<#case x = 2>
     2
<#default>
     d
</#switch> 
如果x=1 输出 1 2, x=2输出 2, x=3 输出d

4.4 if 
这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下:

示例如下:
<#assign age=23>
<#if (age>60)>老年人
<#elseif (age>40)>中年人
<#elseif (age>20)>青年人
<#else> 少年人
</#if>

4.5 else 
这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下:

示例如下:
<#assign age=23>
<#if (age>60)>老年人
<#elseif (age>40)>中年人
<#elseif (age>20)>青年人
<#else> 少年人
</#if>

4.6 elseif 
这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下:

示例如下:
<#assign age=23>
<#if (age>60)>老年人
<#elseif (age>40)>中年人
<#elseif (age>20)>青年人
<#else> 少年人
</#if>

4.7 escape 
escape指令导致body区的插值都会被自动加上escape表达式,但不会影响字符串内的插值,只会影响到body内出现的插值。

示例代码:
<#escape x as x?html>
First name:${firstName}
Last name:${lastName}
Maiden name:${maidenName}
</#escape>

4.8 import 
该指令用于导入FreeMarker模板中的所有变量,并将该变量放置在指定的Map对象中。

示例代码:
<#import "/libs/mylib.ftl" as my> 

4.9 include 
include指令的作用类似于JSP的包含指令,用于包含指定页。

示例代码:
<#include "/common/copyright.ftl" encoding=”GBK”> 

4.10 list 
list指令是一个迭代输出指令,用于迭代输出数据模型中的集合。

示例代码:
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x>
${x_index + 1}.${x}<#if x_has_next>,</if>
<#if x="星期四"><#break></#if>
</#list> 

4.11 macro 
macro可以用于实现自定义指令,通过使用自定义指令,可以将一段模板片段定义成一个用户指令。

示例代码:
<#macro book booklist>     //定义一个自定义指令booklist是参数
<#list booklist as book>
   ${book}
</#list>
</#macro> 

4.12 nested 
和macro标签一起使用, 在nested指令中使用循环变量时,可以使用多个循环变量

示例代码:
<#macro repeat count>
<#list 1..count as x>     //使用nested指令时指定了三个循环变量
   <#nested x, x/2, x==count>
</#list>
</#macro>
<@repeat count=4 ; c halfc last>
${c}. ${halfc}<#if last> Last! </#if>
</@repeat>

4.13 noescape 
对于放在escape指令中所有的插值而言,这此插值将被自动加上escape表达式,如果需要指定escape指令中某些插值无需添加escape表达式,则应该使用noescape指令,放在noescape指令中的插值将不会添加escape表达式.

示例代码:
<#escape x as x?html>
First name:${firstName}
<#noescape>Last name:${lastName}</#noescape>
Maiden name:${maidenName}
</#escape>

4.14 noparse 
noparse指令指定FreeMarker不处理该指定里包含的内容。

示例代码:
<#noparse>
<#list books as book>
   <tr><td>${book.name}<td>作者:${book.author}
</#list>
</#noparse> 

4.15 return 
return指令用于结束macro指令,一旦在macro指令中执行了return指令,则FreeMarker不会继续处理macro指令里的内容

示例代码:
<#macro book>
spring
<#return>
j2ee
</#macro>
<@book />
上面的代码输出:spring,而j2ee位于return指令之后,不会输出.

4.16 setting 
用来设置整个系统的一个环境。

示例代码:
${1.2}
<#setting locale="en_US">
${1.2}
输出
1,2
1.2

4.17 switch 
switch , case , default , break这些指令显然是分支指令,作用类似于Java的switch语句

示例代码:
<#switch x>
<#case x = 1>
     1
<#case x = 2>
     2
<#default>
     D
</#switch> 
如果x=1 输出 1 2, x=2输出 2, x=3 输出d

4.18 字符串处理 
各种处理字符串的方法,有:
html安全输出、substring的用法、类似String.split的用法、取得字符串长度、大写输出字符、小写输出字符、首字符大写、首字符小写、去掉字符串前后空格、每个单词的首字符大写、类似String.indexof:、类似String.lastIndexOf 、替换字符串 replace等 

4.19 对象是不是null 
判断对象是不是null
<#if mouse?exists>
       Mouse found
<#else>
也可以直接${mouse?if_exists})输出布尔形

五、FreeMarker内建函数

1、获取集合大小

共 ${testList?size} 条记录

2、转换JSON字符串为对象
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />
开户行:${data.bank}  账号:${data.account}

3、日期格式化
后台封装数据:
dataModel.put("today", new Date());

freemarker函数:
当前日期:${today?date} <br>
当前时间:${today?time} <br>   
当前日期+时间:${today?datetime} <br>        

日期格式化:  ${today?string("yyyy年MM月")}

4、数字转换为字符串

后台封装数据:
map.put("point", 102920122);
freemarker函数c:

我们会发现数字会以每三位一个分隔符显示,有些时候我们不需要这个分隔符,就需要将数字转换为字符串,使用内建函数c
${point?c}

六、FreeMarker运算符
1、空值处理运算符用法为:variable??,如果该变量存在,返回true,否则返回false 

<#if aaa??>
  aaa变量存在
<#else>
  aaa变量不存在
</#if>

2、缺失变量默认值:“!”
我们除了可以判断是否为空值,也可以使用!对null值做转换处理

${aaa!'-'}

在代码中不对aaa赋值,也不会报错了 ,当aaa为null则返回!后边的内容-

3、算数运算符
逻辑运算符有如下几个: 
逻辑与:&& 
逻辑或:|| 
逻辑非:! 
逻辑运算符只能作用于布尔值,否则将产生错误 

4、比较运算符
表达式中支持的比较运算符有如下几个: 
1  =或者==:判断两个值是否相等. 
2  !=:判断两个值是否不等. 
3  >或者gt:判断左边值是否大于右边值 
4  >=或者gte:判断左边值是否大于等于右边值 
5  <或者lt:判断左边值是否小于右边值 
6  <=或者lte:判断左边值是否小于等于右边值 
注意:=和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且FreeMarker是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)> 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: