在 Spring Boot 中使用 FreeMarker 生成静态文件(如 HTML、XML 等)的核心思路是:通过 FreeMarker 模板引擎渲染动态内容后,将结果写入物理文件。以下是完整实现步骤:
1. 添加依赖 & 基础配置
确保已添加 FreeMarker 依赖(与常规 Web 渲染配置相同):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
application.yml
配置:
spring:
freemarker:
template-loader-path: classpath:/templates/
cache: false # 开发时关闭缓存
suffix: .ftl // 指定文件格式 需要以.ftl文件结尾
charset: UTF-8
2. 核心工具类:生成静态文件
创建一个工具类 StaticFileGenerator
,用于封装生成逻辑:
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import java.io.FileWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
@Component
public class StaticFileGenerator {
// 引入Configuration来操作 FreeMarker 生成静态文件
private final Configuration freemarkerConfig;
public StaticFileGenerator(Configuration freemarkerConfig) {
this.freemarkerConfig = freemarkerConfig;
}
/**
* 生成静态文件
* @param templateName 模板文件名(如 "post.ftl")
* @param dataModel 数据模型
* @param outputPath 输出文件路径(如 "/var/www/html/post-123.html")
*/
public void generate(String templateName, Map<String, Object> dataModel, String outputPath) {
try {
// 获取模板
Template template = freemarkerConfig.getTemplate(templateName);
// 渲染内容
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataModel);
// 写入文件
Path path = Paths.get(outputPath);
path.toFile().getParentFile().mkdirs(); // 确保目录存在
try (FileWriter writer = new FileWriter(path.toFile())) {
writer.write(content);
}
} catch (Exception e) {
throw new RuntimeException("生成静态文件失败: " + e.getMessage(), e);
}
}
}
3. 使用场景示例:生成博客文章静态页
场景描述
当用户发布一篇博客时,自动生成对应的静态 HTML 文件(如 post-123.html
)。
步骤 1:定义数据模型
// 示例实体类
@Data // Lombok 注解
public class BlogPost {
private Long id;
private String title;
private String content;
private LocalDateTime createTime;
}
步骤 2:创建 FreeMarker 模板
模板文件 src/main/resources/templates/post.ftl
:
<!DOCTYPE html>
<html>
<head>
<title>${post.title}</title>
<meta charset="UTF-8">
</head>
<body>
<h1>${post.title}</h1>
<div class="time">发布时间:${post.createTime?string("yyyy-MM-dd HH:mm")}</div>
<div class="content">${post.content}</div>
</body>
</html>
步骤 3:在 Service 中调用生成逻辑
@Service
public class BlogService {
private final StaticFileGenerator staticFileGenerator;
public BlogService(StaticFileGenerator staticFileGenerator) {
this.staticFileGenerator = staticFileGenerator;
}
public void publishPost(BlogPost post) {
// 保存到数据库的逻辑(此处省略)...
// 构建数据模型
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("post", post);
// 生成静态文件(假设输出到项目的 static/posts 目录) 指定的文件目录
String outputPath = "src/main/resources/static/posts/post-" + post.getId() + ".html";
staticFileGenerator.generate("post.ftl", dataModel, outputPath);
}
}
4. 触发生成
通过 Controller 或定时任务触发生成逻辑:
@RestController
@RequestMapping("/posts")
public class BlogController {
private final BlogService blogService;
public BlogController(BlogService blogService) {
this.blogService = blogService;
}
@PostMapping
public ResponseEntity<String> createPost(@RequestBody BlogPost post) {
blogService.publishPost(post);
return ResponseEntity.ok("文章发布成功,静态页已生成!");
}
}
5. 访问静态文件
生成的静态文件默认会被 Spring Boot 识别为静态资源(因为放在 src/main/resources/static
目录下)。
访问地址示例:
http://localhost:8080/posts/post-123.html
关键注意事项
1. 路径配置
- 输出路径:建议将静态文件生成到独立目录(如
/var/www/html
),而非项目资源目录,避免打包后路径混乱。 - 权限问题:确保应用有权限写入目标目录。
2. 动态更新
- 模板修改:若修改了模板文件,需重新生成静态文件才能生效。
- 数据更新:如果数据变更,需重新生成关联的静态文件(或设置版本号机制)。
3. 性能优化
- 批量生成:对大量文件生成时,可结合线程池提高效率。
- 缓存控制:生产环境开启 FreeMarker 缓存(
spring.freemarker.cache=true
)。
高级用法
1. 结合定时任务
使用 @Scheduled
定期生成静态文件(如每日凌晨更新排行榜):
@Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行
public void generateDailyReport() {
Map<String, Object> data = reportService.getDailyData();
staticFileGenerator.generate("report.ftl", data, "/reports/daily-report.html");
}
2. 事件驱动生成
通过 Spring 事件机制,在数据变更时触发生成:
// 定义事件
public class PostPublishedEvent extends ApplicationEvent {
private final BlogPost post;
public PostPublishedEvent(Object source, BlogPost post) {
super(source);
this.post = post;
}
// getter
}
// 发布事件(在 Service 中)
applicationEventPublisher.publishEvent(new PostPublishedEvent(this, post));
// 监听事件
@Component
public class StaticFileGeneratorListener {
@EventListener
public void handlePostPublished(PostPublishedEvent event) {
staticFileGenerator.generate(...);
}
}
总结
通过 FreeMarker 生成静态文件的核心步骤:
- 模板设计:编写
.ftl
模板文件,定义动态占位符和逻辑。 - 数据准备:构建与模板匹配的数据模型(Map 或 Java 对象)。
- 渲染写入:调用 FreeMarker 渲染模板,将结果写入物理文件。
- 访问管理:通过 HTTP 直接访问静态文件或集成到 CDN。
此方案适用于需要预生成静态内容以提高性能的场景(如 SEO 优化、高并发页面)。