一、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> |