FreeMarker
最后修改于 2024 年 1 月 27 日
这是 FreeMarker Java 模板引擎的入门教程。我们介绍 FreeMarker 模板引擎,并创建几个控制台和 Web 应用程序。Maven 用于构建我们的示例。NetBeans 用于管理应用程序。
目录
FreeMarker 是 Java 编程语言的模板引擎。模板使用 FreeMarker 模板语言 (FTL) 编写。
FreeMarker 模板引擎
模板引擎将静态数据与动态数据组合以生成内容。模板是内容的中间表示形式;它指定如何生成输出。
模板引擎的优点是
- 关注点分离,
- 避免代码重复,
- 更易于在视图之间切换,
- 可重用性。
按照惯例,FreeMarker 模板文件具有 .ftl 扩展名。
FreeMarker 不仅限于 HTML 页面的模板;它可用于生成电子邮件、配置文件、源代码等。
implementation 'org.freemarker:freemarker:2.3.31'
我们在 Gradle 项目中使用此 FreeMarker 依赖项。
FreeMarker 插值
插值是放在 ${ } 字符之间的表达式。FreeMarker 会将输出中的插值替换为花括号内表达式的实际值。
在以下示例中,我们使用 FreeMarker 模板文件生成简单的文本输出。
package com.zetcode;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
public class FreeMarkerConsoleEx {
public static void main(String[] args) throws IOException,
TemplateException {
var cfg = new Configuration(new Version("2.3.31"));
cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/views");
cfg.setDefaultEncoding("UTF-8");
Template template = cfg.getTemplate("test.ftlh");
Map<String, Object> templateData = new HashMap<>();
templateData.put("msg", "Today is a beautiful day");
try (StringWriter out = new StringWriter()) {
template.process(templateData, out);
System.out.println(out.getBuffer().toString());
out.flush();
}
}
}
该示例将一个简单的文本打印到控制台。最终文本由模板引擎处理。
var cfg = new Configuration(new Version("2.3.31"));
Configuration 用于设置 FreeMarker 设置;它将 FreeMarker 库的版本作为参数。
cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/views");
setClassForTemplateLoading 设置将用于加载模板的类的方法。 模板位于 src/main/resources 目录的 views 子目录中。
Template template = cfg.getTemplate("test.ftlh");
使用 getTemplate 方法,我们检索 test.ftlh 模板文件。
Map<String, Object> templateData = new HashMap<>();
templateData.put("msg", "Today is a beautiful day");
创建数据模型。来自模型的数据将动态地放置到 FreeMarker 模板文件中。
try (StringWriter out = new StringWriter()) {
template.process(templateData, out);
System.out.println(out.getBuffer().toString());
out.flush();
}
process 方法执行模板,使用提供的数据模型并将生成的输出写入提供的 writer。
The message is: ${msg}
test.ftlh 模板文件包含一个插值;它将被生成的字符串替换。
version '1.0'
apply plugin: 'java'
apply plugin: 'application'
sourceCompatibility = 17
mainClassName = "com.zetcode.FreeMarkerConsoleEx"
repositories {
mavenCentral()
}
dependencies {
implementation 'org.freemarker:freemarker:2.3.31'
}
这是 Gradle 构建文件。
$ gradle run -q The message is: Today is a beautiful day
FreeMarker list 指令
#list 指令列出数据的集合。
下一个示例生成汽车列表。
package com.zetcode;
public class Car {
private String name;
private int price;
public Car() {
}
public Car(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
我们有一个 Car bean。 它有两个属性:名称和价格。
package com.zetcode;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class FreeMarkerConsoleEx {
public static void main(String[] args) throws IOException,
TemplateException {
var cfg = new Configuration(new Version("2.3.31"));
cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/views");
cfg.setDefaultEncoding("UTF-8");
Template template = cfg.getTemplate("test.ftlh");
Map<String, Object> templateData = new HashMap<>();
var c1 = new Car("Audi", 52642);
var c2 = new Car("Volvo", 29000);
var c3 = new Car("Skoda", 9000);
var cars = new ArrayList<>();
cars.add(c1);
cars.add(c2);
cars.add(c3);
templateData.put("cars", cars);
try (StringWriter out = new StringWriter()) {
template.process(templateData, out);
System.out.println(out.getBuffer().toString());
out.flush();
}
}
}
此示例是一个 Java 控制台程序,它使用 FreeMarker 动态创建包含汽车列表的文本输出。
Map<String, Object> templateData = new HashMap<>();
var c1 = new Car("Audi", 52642);
var c2 = new Car("Volvo", 29000);
var c3 = new Car("Skoda", 9000);
var cars = new ArrayList<>();
cars.add(c1);
cars.add(c2);
cars.add(c3);
templateData.put("cars", cars);
在这里,我们创建一个 Car 对象列表并将其放入数据模型中。
<#list cars as car>
${car.name}: ${car.price}
</#list>
模板文件包含一个 #list 指令,该指令打印汽车对象的属性;使用点字符访问属性。
$ gradle run -q Audi: 52,642 Volvo: 29,000 Skoda: 9,000
FreeMarker 指令
FreeMarker 指令是执行操作的特殊标记。有两种指令:内置指令和自定义指令。
<#assign> 标签创建一个新的纯变量。可以使用 ${} 构造访问它。变量在模板中创建。如果数据模型中存在同名变量,则模板变量会隐藏它。
<#assign name = "Robert">
His name is ${name}.
<#assign> 指令创建一个新的 name 变量。变量的值使用 ${name} 语法打印。
His name is Robert.
该示例打印此行。
可以使用 <#if>、<#elseif> 和 <#else> 指令完成模板部分的条件处理。
<#assign value = 4> <#if value < 0> The number is negative <#elseif value == 0> The number is zero <#else> The number is positive </#if>
该示例创建一个新的 value 变量,并使用条件指令来测试该值。
The number is positive
<#list> 指令用于遍历序列。
<#assign colours = ["red", "green", "blue", "yellow"]>
<#list colours as col>
${col}
</#list>
在该示例中,我们向 colours 变量分配一个新的颜色名称序列。 <#list> 指令遍历该集合并打印每个项目。
red green blue yellow
该示例给出此输出。
<#assign items = {"pens": 3, "cups": 2, "tables": 1}>
<#list items?values as v>
${v}
</#list>
<#list items?keys as k>
${k}
</#list>
在此示例中,我们创建一个哈希变量,并使用 <#list> 输出哈希的值和键。
3 2 1 pens cups tables
该示例给出此输出。
当我们使用对空白不敏感的格式(例如 HTML 或 XML)时,<#compress> 指令会删除多余的空白。
<#assign value="\t\tweather\n\n">
<#compress>
${value}
Today is a wonderful day.
1 2 3 4 5
</#compress>
我们有带有空格、制表符和换行符的文本。
weather Today is a wonderful day. 1 2 3 4 5
该程序删除了所有多余的空白。
FreeMarker 与 Spark
在以下示例中,我们将把 FreeMarker 模板引擎集成到我们的 Spark 应用程序中。
build.gradle
src
└── main
├── java
│ └── com
│ └── zetcode
│ └── SparkFreeMarkerEx.java
└── resources
└── views
└── hello.ftlh
这是项目的目录结构。
apply plugin: 'java'
apply plugin: 'application'
archivesBaseName = "spark-freemarker"
version = '1.0'
mainClassName = "com.zetcode.SparkFreemarkerEx"
repositories {
mavenCentral()
}
dependencies {
implementation 'com.sparkjava:spark-core:2.9.4'
implementation 'com.sparkjava:spark-template-freemarker:2.7.1'
implementation 'org.slf4j:slf4j-simple:1.7.36'
}
在这里,我们有 Gradle 构建文件,其中包括 spark-template-freemarker 依赖项。
package com.zetcode;
import freemarker.template.Configuration;
import freemarker.template.Version;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import spark.ModelAndView;
import spark.Request;
import spark.Response;
import static spark.Spark.get;
import spark.template.freemarker.FreeMarkerEngine;
public class SparkFreemarkerEx {
public static void main(String[] args) throws IOException {
Configuration conf = new Configuration(new Version(2, 3, 26));
conf.setClassForTemplateLoading(SparkFreemarkerEx.class, "/views");
get("/hello/:name/", SparkFreemarkerEx::message, new FreeMarkerEngine(conf));
}
public static ModelAndView message(Request req, Response res) {
Map<String, Object> params = new HashMap<>();
params.put("name", req.params(":name"));
return new ModelAndView(params, "hello.ftlh");
}
}
我们为 FreeMarker 设置相同的应用程序。
Configuration conf = new Configuration(new Version(2, 3, 26)); conf.setClassForTemplateLoading(SparkFreemarkerEx.class, "/views");
我们使用 Configuration 类配置 FreeMarker。 模板文件将放置到 views 目录中,该目录必须位于类路径上。
get("/hello/:name/", SparkFreemarkerEx::message, new FreeMarkerEngine(conf));
FreeMarkerEngine 传递给 get 方法。
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>Hello ${name}</p>
</body>
</html>
这是 hello.ftlh 模板文件;它引用了 ModelAndView 对象传递的 name 变量。
$ curl localhost:4567/hello/Lucy/
<!DOCTYPE html>
<html>
<head>
<title>Home page</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<p>Hello Lucy</p>
</body>
</html>
Spring Boot FreeMarker
在下一个应用程序中,我们将 FreeMarker 集成到 Spring Boot Web 应用程序中。
build.gradle
src
└── main
├── java
│ └── com
│ └── zetcode
│ ├── Application.java
│ └── controller
│ └── MyController.java
└── resources
└── templates
├── hello.ftlh
└── index.ftlh
这是项目结构。
plugins {
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.zetcode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
}
这是 Gradle 构建文件。 它包含 Spring Boot 和 FreeMarker 的依赖项。 无需在 Spring Boot 中配置 FreeMarker。 在 POM 文件中找到 FreeMarker 依赖项后,Spring Boot 会自动处理配置。
package com.zetcode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Application 设置 Spring Boot 应用程序。 @SpringBootApplication 注释将该类定义为配置类,启用自动配置,并启用组件扫描。
package com.zetcode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class MyController {
@GetMapping("/")
public String index(Model model) {
return "index";
}
@GetMapping("/hello")
public String hello(Model model, @RequestParam(value="msg", required=false,
defaultValue="Freemarker") String msg) {
model.addAttribute("message", msg);
return "hello";
}
}
这是 Spring Boot Web 应用程序的控制器类。 控制器有两个映射。 第一个映射解析为 index.ftl 文件,第二个映射解析为 hello.ftlh 文件。
<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Form</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<form action="/hello" method="get">
<p>Message: <input type="text" name="msg"></p>
<p>
<input type="submit" value="Submit">
<input type="reset" value="Reset">
</p>
</form>
</body>
</html>
这是 index.ftlh 文件。 它有一个 HTML 表单,可将消息发送到服务器。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Freemarker example</title>
</head>
<body>
<p>${message}<p>
</body>
</html>
服务器以消息响应客户端。 响应由 hello.ftlh 模板文件创建。
$ ./gradlew bootRun
Spring Boot 启动一个嵌入式 Tomcat 服务器,在端口 8080 上侦听。
来源
在本教程中,我们使用了 FreeMarker 模板引擎。
作者
列出所有Java教程。