一、FreeMarker介绍
FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。
FreeMarker 被设计用来生成 HTML Web 页面,特别是基于 MVC 模式的应用程序,将视图从业务逻辑中抽离处理,业务中不再包括视图的展示,而是将视图交给 FreeMarker 来输出。
二、FreeMarker模板
FTL (代表FreeMarker模板语言--FreeMarker Template Language)。 这是为编写模板设计的非常简单的编程语言。
模板(FTL编程)是由如下部分混合而成的:
-
文本:文本会照着原样来输出。
-
插值:这部分的输出会被计算的值来替换。插值由
${and}所替换(或者#{and},这种风格已经不建议再使用了)。 -
FTL 标签:FTL标签和HTML标签很相似,但是它们却是给FreeMarker的指示, 而且不会打印在输出内容中。
-
注释:注释和HTML的注释也很相似,但它们是由
<#--和-->来分隔的。注释会被FreeMarker直接忽略, 更不会在输出内容中显示。下面是一个具体的模板例子:
<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 animal>[BR]
<li>${animal.name} for ${animal.price} Euros[BR]
</#list>[BR]
</ul>[BR]
</body>[BR]
</html>
|
注意下面几点
FTL是区分大小写的。 list 是指令的名称而 List 就不是。类似地 ${name} 和 ${Name} 或 ${NAME} 也是不同的。
插值 仅仅可以在 文本 中使用。注释 可以放在 FTL 标签 和 插值中。比如:
<h1>Welcome ${user <#-- The name of user -->}!</h1>[BR]
<p>We have these animals:[BR]
<ul>[BR]
<#list <#-- some comment... --> animals as <#-- again... --> animal>[BR]
|
三、FreeMarker数据模型
数据模型的基本结构是树状的。 这棵树可以很复杂,并且可以有很大的深度,,例如:
(root) | +- animals | | | +- mouse | | | | | +- size = "small" | | | | | +- price = 50 | | | +- elephant | | | | | +- size = "large" | | | | | +- price = 5000 | | | +- python | | | +- size = "medium" | | | +- price = 4999 | +- message = "It is a test" | +- misc | +- foo = "Something" |
上图中的变量扮演目录的角色(比如 root, animals, mouse, elephant, python, misc) 被称为 hashes (哈希表或哈希,译者注)。哈希表存储其他变量(被称为 子变量), 它们可以通过名称来查找(比如 "animals", "mouse" 或 "price")。
存储单值的变量 (size, price, message 和 foo) 称为 scalars (标量,译者注)。
如果要在模板中使用子变量, 那应该从根root开始指定它的路径,每级之间用点来分隔开。要访问 mouse 的 price ,要从root开始,首先进入到 animals ,之后访问 mouse ,最后访问 price 。就可以这样来写 animals.mouse.price。
另外一种很重要的变量是 sequences (序列,译者注)。 它们像哈希表那样存储子变量,但是子变量没有名字,它们只是列表中的项。
总结:
-
数据模型可以被看成是树形结构。
-
标量用于存储单一的值。这种类型的值可以是字符串,数字,日期/时间或者是布尔值。
-
哈希表是一种存储变量及其相关且有唯一标识名称的容器。
-
序列是存储有序变量的容器。存储的变量可以通过数字索引来检索,索引通常从0开始。
四、模板 + 数据模型 = 输出
模板文件存放在Web服务器上,就像通常存放静态HTML页面那样。当有人来访问这个页面, FreeMarker将会介入执行,然后动态转换模板,用最新的数据内容替换模板中 ${...} 的部分, 之后将结果发送到访问者的Web浏览器中。访问者的Web浏览器就会接收到例如第一个HTML示例那样的内容 (也就是没有FreeMarker指令的HTML代码),访问者也不会察觉到服务器端使用的FreeMarker。
为模板准备的数据整体被称作为 数据模型。
总的来说,模板和数据模型是FreeMarker来生成输出(比如第一个展示的HTML)所必须的:
模板 + 数据模型 = 输出
五、Freemarker开发实战
1.maven项目引入相关工程依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
|
2. 创建模板文件(ftl文件)
testTemplate.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Freemarker入门测试</h1>
${name} ----- ${message}
<#list goodsList as goods>
商品名称: ${goods.name} 价格:${goods.price} <br>
</#list>
</body>
</html>
|
3.生成文件
使用步骤:
- 创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是freemarker 的版本号。
- 设置模板文件所在的路径。
- 设置模板文件使用的字符集。一般就是 utf-8
- 加载一个模板,创建一个模板对象。
- 创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
- 创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
- 调用模板对象的 process 方法输出文件。
- 关闭流。
package com.freemarker.test;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FreemarkerTest {
public static void main(String[] args) throws IOException, TemplateException {
//1.创建freemarker的配置对象
Configuration configuration = new Configuration(Configuration.getVersion());
//2.设置模板文件所在目录
configuration.setDirectoryForTemplateLoading(new File("./src/main/webapp/WEB-INF/ftl/"));
//3.设置字符集
configuration.setDefaultEncoding("utf-8");
//4.加载模板
Template template = configuration.getTemplate("testTemplate.ftl");
//5.准备模板文件中所需的数据,一般通过map构造
Map<String,Object> map = new HashMap<>();
map.put("name","chenwt");
map.put("message","你好啊!!!freemarker");
List goodsList = new ArrayList();
Map goods1 = new HashMap();
goods1.put("price","1.00");
goods1.put("name","apple");
goodsList.add(goods1);
Map goods2 = new HashMap();
goods2.put("price","2.00");
goods2.put("name","peach");
goodsList.add(goods2);
map.put("goodsList",goodsList);
//6.创建Writer对象,用于输出静态文件
Writer out = new FileWriter("./src/main/webapp/WEB-INF/html/test.html");
//7.输出文件
template.process(map,out);
//8.关闭流
out.close();
}
}
|
执行完,输出的html页面如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Freemarker入门测试</h1>
chenwt ----- 你好啊!!!freemarker
商品名称: apple 价格:1.00 <br>
商品名称: peach 价格:2.00 <br>
</body>
</html>
|
六、Alogic框架集成FreeMarker教程
1.maven项目引入相关工程依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
|
2. 创建模板文件(ftl文件)
testTemplate.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Freemarker入门测试</h1>
${name} ----- ${message}
<#list goodsList as goods>
商品名称: ${goods.name} 价格:${goods.price} <br>
</#list>
</body>
</html>
|
3、alogic项目新增一个创建Configuration单例对象的工厂类
注意:Configuration实例对象是一个单例对象。不需要重复创建Configuration实例; 它的代价很高,尤其是会丢失缓存。Configuration实例就是应用级别的单例。
package com.alogic.freemarker;
import com.anysoft.util.Factory;
import com.anysoft.util.PropertiesConstants;
import com.anysoft.util.Settings;
import java.io.File;
import java.io.IOException;
import freemarker.template.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FreeMarkerConfigurationFactory extends Factory<Configuration> {
private volatile static Configuration INSTANCE = null;
public FreeMarkerConfigurationFactory() {
}
public static Configuration getDefault() throws IOException {
if (INSTANCE == null) {
synchronized(Configuration.class) {
if (INSTANCE == null) {
Settings p = Settings.get();
String templateDir = PropertiesConstants.getString(p, "freemarker.template.dir", "./src/main/webapp/WEB-INF/ftl/");
String freemarkerEncode = PropertiesConstants.getString(p, "freemarker.encoder", "utf-8");
INSTANCE = new Configuration(Configuration.getVersion());
INSTANCE.setDirectoryForTemplateLoading(new File(templateDir));
INSTANCE.setDefaultEncoding(freemarkerEncode);
}
}
}
return INSTANCE;
}
}
|
4、项目新增freemarker插件,输入是模板名称和数据模型(json格式),输出为html文件
package com.alogic.freemarker.xscript;
import com.alogic.freemarker.FreeMarkerConfigurationFactory;
import com.alogic.xscript.AbstractLogiclet;
import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.doc.XsObject;
import com.anysoft.util.IOTools;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import com.fasterxml.jackson.databind.ObjectMapper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.util.Map;
public class FreeMarker extends AbstractLogiclet {
protected String templateName;
protected String outputDir="./src/main/webapp/WEB-INF/html/";
protected String outputName="";
protected String dataModel = "";
public FreeMarker(String tag, Logiclet p) {
super(tag, p);
}
public void configure(Properties p){
super.configure(p);
outputDir = PropertiesConstants.getRaw(p,"outputDir", outputDir);
outputName = PropertiesConstants.getRaw(p,"outputName", "");
templateName = PropertiesConstants.getRaw(p,"templateName", "");
dataModel = PropertiesConstants.getRaw(p, "dataModel", dataModel);
}
@Override
protected void onExecute(XsObject root, XsObject current, LogicletContext ctx, ExecuteWatcher watcher) {
String templateNameVal = ctx.transform(templateName);
Configuration configuration;
Writer out = null;
Map<String, Object> dataModelMap;
ObjectMapper objectMapper = new ObjectMapper();
String dataModelData = ctx.transform(dataModel);
if (StringUtils.isNotEmpty(templateNameVal)&&StringUtils.isNotEmpty(dataModelData)){
try {
configuration = FreeMarkerConfigurationFactory.getDefault();
dataModelMap = objectMapper.readValue(dataModelData, Map.class);
Template template = configuration.getTemplate(templateNameVal);
String outputDirVal = ctx.transform(outputDir);
String outputNameVal = ctx.transform(outputName);
out =new FileWriter(outputDirVal + outputNameVal);
template.process(dataModelMap, out);
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}finally {
try {
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
IOTools.close(new Closeable[]{out});
}
}
}
}
|
5、服务中引用freemarker插件
<?xml version="1.0" encoding="utf-8"?>
<service
id = "Test"
acGroupId = "any"
visible="public"
>
<script>
<using xmlTag="ns" module="com.alogic.freemarker.xscript.NS"/>
<using xmlTag="FreeMarker" module="com.alogic.freemarker.xscript.FreeMarker"/>
<ns>
<log msg="test 新项目"/>
<obj tag="requestDoc">
<get id="name" value="张三"/>
<get id="message" value="欢迎来到神奇的品优购世界!"/>
<array tag="goodsList">
<array-item>
<get id="name" value="ppp" />
<get id="price" value="1.00" />
</array-item>
<array-item>
<get id="name" value="ppp2" />
<get id="price" value="2.00" />
</array-item>
</array>
<setAsJson id="iamRequestBody"/>
</obj>
<rem tag="requestDoc"/>
<log msg="requestDoc ${iamRequestBody}"/>
<FreeMarker templateName="testTemplate.ftl" outputName="test.html" dataModel="${iamRequestBody}"/>
</ns>
</script>
</service>
|