在前后端分离架构大行其道的今天,模板引擎依然有着重要的地位和不可代替性。
Freemarker 是一款界开源的老牌模板引擎,使用 Java 开发,Spring 官方对 Freemarker 提供了支持。本文将会带你学习如何在 Spring Boot 中整合 Freemarker。
创建项目 {#创建项目}
在 pom.xml
添加 spring-boot-starter-web
和 spring-boot-starter-freemarker
依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
编写模板 {#编写模板}
根据 Spring Boot 约定,模板文件应该放在 src/main/resources
目录下的 templates
目录中。
在 templates
目录中,创建 index
文件夹,用于存放渲染主页的 index.ftl
模板视图,如下:
其中 index.ftl
内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Freemarker</title>
</head>
<body>
Hello ${title}!
</body>
</html>
如上,模板只是简单地输出了 Model 中的 title
属性。
配置属性 {#配置属性}
Spring Boot 提供了很多配置属性,可用于在 application.yaml | properties
中定制 Freemarker。 这些属性都以 spring.freemarker
开头。
常用配置如下:
spring:
freemarker:
# 启用 freemarker 模板
enabled: true
# 是否缓存
cache: false
# Content Type
content-type: text/html
# 编码
charset: utf-8
# 模板后缀
suffix: .ftl
# 引用 request 的属性名称
request-context-attribute: request
# 是否暴露 request 域中的属性
expose-request-attributes: false
# 是否暴露session域中的属性
expose-session-attributes: false
# request 域中的属性是否可以覆盖 controller 的 model 的同名项。默认 false,如果发生同名属性覆盖的情况会抛出异常
allow-request-override: true
# session 域中的属性是否可以覆盖 controller 的 model 的同名项。默认 false,如果发生同名属性覆盖的情况会抛出异常
allow-session-override: true
# 暴露官方提供的宏
expose-spring-macro-helpers: true
# 启动时检查模板位置是否有效
check-template-location: true
# 优先加载文件系统的模板
prefer-file-system-access: true
# 模板所在位置(目录)
template-loader-path:
- classpath:/templates/
settings:
datetime_format: yyyy-MM-dd HH:mm:ss # date 输出格式化
template_update_delay: 30m # 模板引擎刷新时间
default_encoding: utf-8 # 默认编码
完整的配置信息,可参阅官方文档。
spring.freemarker.expose-spring-macro-helpers
设置为 true
可以暴露 spring 官方提供的宏。这些宏提供了国际化、表单绑定等等功能。
这些宏定义在 spring-webmvc
模块的 /org/springframework/web/servlet/view/freemarker/spring.ftl
文件中。你可以在模板中使用 #import
指令导入、使用,如下:
<#import "/spring.ftl" as spring/>
"/spring.ftl" 必须以 "/" 开头,否则会异常:
Caused by: freemarker.template.TemplateNotFoundException: Template not found for name "index/spring.ftl". The name was interpreted by this TemplateLoader: MultiTemplateLoader(loader1 = FileTemplateLoader(baseDir="C:\eclipse\eclipse-jee-2023-09-R-win32-x86_64\app\springdoc-demo\target\classes\templates", canonicalBasePath="C:\eclipse\eclipse-jee-2023-09-R-win32-x86_64\app\springdoc-demo\target\classes\templates\"), loader2 = ClassTemplateLoader(resourceLoaderClass=org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer, basePackagePath="" /* relatively to resourceLoaderClass pkg */)). at freemarker.template.Configuration.getTemplate(Configuration.java:2957) ~[freemarker-2.3.32.jar:2.3.32] at freemarker.core.Environment.getTemplateForInclusion(Environment.java:3062) ~[freemarker-2.3.32.jar:2.3.32] at freemarker.core.Environment.getTemplateForInclusion(Environment.java:3024) ~[freemarker-2.3.32.jar:2.3.32] at freemarker.core.Environment.getTemplateForImporting(Environment.java:3186) ~[freemarker-2.3.32.jar:2.3.32] at freemarker.core.Environment.importLib(Environment.java:3171) ~[freemarker-2.3.32.jar:2.3.32] at freemarker.core.Environment.importLib(Environment.java:3135) ~[freemarker-2.3.32.jar:2.3.32] at freemarker.core.LibraryLoad.accept(LibraryLoad.java:65) ~[freemarker-2.3.32.jar:2.3.32] ... 55 common frames omitted
更详细的用法你可以参考 官方文档。
spring.freemarker.template-loader-path
是一个数组,支持定义多个模板目录。不仅支持 classpath 目录,也支持本地磁盘目录,如:file:${user.dir}/templates/
。
定义 Controller {#定义-controller}
创建 IndexController
,返回 ModelAndView
,渲染主页模板视图:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@RequestMapping("/")
@Controller
public class IndexController {
@GetMapping
public ModelAndView index () {
ModelAndView modelAndView = new ModelAndView("index/index");
// 添加 title 属性到 Model
modelAndView.addObject("title", "Freemarker");
return modelAndView;
}
}
在 ModelAndView
对象的构造函数中指定要渲染的视图(模板),不需要添加 .ftl
后缀,因为已经统一在配置文件中定义了。
启动应用,使用 curl
(浏览器也可以)访问 localhost:8080
,响应如下:
$ curl localhost:8080
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Freemarker</title>
</head>
<body>
Hello Freemarker!
</body>
</html>
如果所见,成功地渲染了 index.ftl
,输出了 Model 中的 title
属性。
Configuration {#configuration}
有时候,你可能需要对模板进行深层次的定制,如:设置自定义函数、定义全局变量。
那么你可以在任意 Bean 中通过 IOC 注入:freemarker.template.Configuration
对象,进行自定义设置。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import freemarker.template.TemplateModelException;
import jakarta.annotation.PostConstruct;
@Configuration
public class FreemarkerConfiguration {
// 注入 Configuration
@Autowired
private freemarker.template.Configuration configuration;
@PostConstruct
public void configuration() throws TemplateModelException {
// 添加全局变量
this.configuration.setSharedVariable("app", "Spring Boot");
}
}