I. 简介
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。 在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。 这种方式通常被称为 MVC (模型 视图 控制器) 模式,对于动态网页来说,是一种特别流行的模式。
上图如果Template换成Jsp,对于大多数JavaEE开发者来说,会显得非常熟悉。FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。 它完全可以替代Jsp,实现页面的静态化。
II. 入门程序
需求
利用FreeMarker 实现一个网站用户登录显示用户信息。
流程
- 添加Jar包
- 创建Configration对象
- 创建模板
- 获取模板
- 创建模板需要的数据
- 合并模板与数据
创建工程
FreeMarker 不依赖于web容器,所以普通的java项目也可以使用FreeMarker 。FreeMarker 的依赖包下载可以在官网进行获取,目前最新为FreeMarker 2.3.28版本。
利用IDEA创建工程,如果是普通项目则将 freemarker.jar
添加的 lib
目录下。如果创建为Maven项目,则可以在pom文件中添加:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
如果是结合Spring使用FreeMarker,则需要额外添加依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
创建Configration对象
首先,需要创建一个 freemarker.template.Configuration
实例, 然后对其属性进行设置。Configuration
实例是存储 FreeMarker 应用级设置的核心部分。同时,它也处理创建和缓存预解析模板(比如 Template
对象)的工作。
Configuration
的创建代价很高,不需要重复创建实例,尤其是会丢失缓存。Configuration
实例就是应用级别的单例,不管一个系统有多少独立的组件来使用 FreeMarker, 它们都会使用他们自己私有的 Configuration
实例。
只需要在应用(可能是servlet)生命周期的开始执行一次:
// 创建Configuration实例,指定版本
Configuration configuration = new Configuration(Configuration.getVersion());
try {
// 指定configuration对象模板文件存放的路径
configuration.setDirectoryForTemplateLoading(new File("/where/you/store/templates"));
// 设置config的默认字符集,一般是UTF-8
configuration.setDefaultEncoding("UTF-8");
// 设置错误控制器
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
} catch (IOException e) {
e.printStackTrace();
}
关于错误控制器,FreeMarker 四个预先编写的错误控制器:
- DEBUG_HANDLER:打印堆栈信息和重新抛出异常,这是默认的异常控制器。
- HTML_DEBUG_HANDLER:和DEBUG_HANDLER相同,但是可以格式化堆栈跟踪信息。HTML页面,建议使用它而不是DEBUG_HANDLER。
- IGNORE_HANDLER:简单地忽略所有异常。它对处理异常没有任何作用,也不会重新抛出异常。
- RETHROW_HANDLER: 简单重新抛出所有异常而不会做其他的事情。这个控制器对Web应用很好,因为它在生成的页面发生错误的情况下,给了对Web应用的更多的控制权。
创建模板
在 /where/you/store/templates
文件夹下创建模板文件 hello.ftl
。
<html>
<head>
<title>欢迎 ${user.username}!</title>
</head>
<body>
<h1>欢迎 ${user.username}!</h1>
<h2>年龄: ${user.age}</h2>
<h2>${user.record.id}:${user.record.name}</h2>
</body>
</html>
获取模板
模板代表了 freemarker.template.Template
实例。 典型的做法是直接使用 Configuration
实例的 getTemplate()
方法获取一个 Template
实例,例如:
// 获取模版
Template template = configuration.getTemplate("hello.ftl");
当调用这个方法的时候,将会创建一个 hello.ftl
的 Template
实例,通过读取 */where/you/store/templates/hello.ftl
文件,之后解析(编译)它。Template
实例以解析后的形式存储模板, 而不是以源文件的文本形式。
Configuration
会缓存 Template
实例,当需要再次获得 hello.ftl
的时候,它可能不再读取和解析模板文件了, 而只是返回第一次获取的 Template
实例。
准备数据
需要给模板的数据往往来自真实的业务数据,可能从数据库、文件等获得。在JavaEE中往往有单独的Service和Dao层帮助我们准备好需要的数据。数据的包装形式往往是JavaBean。
- 使用
java.lang.String
来构建字符串。 - 使用
java.lang.Number
来派生数字类型。 - 使用
java.lang.Boolean
来构建布尔值。 - 使用
java.util.List
或Java数组来构建序列。 - 使用
java.util.Map
来构建哈希表。 - 使用自定义的bean类来构建哈希表,bean中的项和bean的属性对应。
这里我们定义两个类,一个是User类,一个是用户记录Record类。
public class User {
private String username;
private int age;
private Record record;
/** setter and getter **/
}
public class Record {
private long id;
private String name;
/** setter and getter **/
}
下面是构建这个数据模型的Java代码片段:
// 准备数据
Map<String, User> map = new HashMap<>();
Record record = new Record();
record.setId(1L);
record.setName("记录一");
User user = new User();
user.setUsername("小明");
user.setAge(18);
user.setRecord(record);
map.put("user", user);
合并模板与数据
数据模型+模板=输出,这一过程是由模板的 process
方法完成的。它需要数据和 Writer
对象两部分作为参数,然后向 Writer
对象写入产生的内容。这里创建一个Writer对象