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教程。