You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot集成Freemarker:如何获取自定义加载的原始模板对象?

问题:Spring Boot + Freemarker自定义数据库加载器,如何获取原始模板对象避免重复查询?

我正在使用Spring Boot和Freemarker,通过自定义加载器从数据库加载模板,自定义加载器代码如下:

public class ContentDbLoader implements TemplateLoader {
 @Inject private TemplateRepository templateRepository;
 @Override public void closeTemplateSource(Object templateSource) throws IOException { return; }
 @Override public Object findTemplateSource(String name) throws IOException { return getTemplateFromName(name); }
 @Override public long getLastModified(Object templateSource) { MyTemplate template = (MyTemplate) templateSource; template = templateRepository.findOne(template.getId()); return template.getLastModifiedDate().toEpochMilli(); }
 @Override public Reader getReader(Object templateSource, String encoding) throws IOException { return new StringReader(((Template) templateSource).getContent()); }
 private MyTemplate getTemplateFromName(String name) { //my logic }
}

模板实体类代码如下:

@Entity
@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "channel", "communicationType", "country_id" }) })
public class Template extends AbstractEntity {
 private static final long serialVersionUID = 8405971325717828643L;
 @NotNull @Column(nullable = false) @Enumerated(EnumType.STRING)
 private Channel channel;
 @NotNull @Column(nullable = false) @Enumerated(EnumType.STRING)
 private CommunicationType communicationType;
 @Size(max = 255) private String subject;
 @NotNull @Column(nullable = false) @Lob private String content;
 @NotNull @Column(nullable = false) private String sender;
 @NotNull @ManyToOne(fetch = FetchType.LAZY, optional = false)
 private Country country;

如上述代码所示,我从数据库返回MyTemplate对象。当调用contentFreemarkerConfig.getTemplate(CommunicationType.WALLET_BALANCE_THRESHOLD + "|" + Channel.EMAIL, locale)获取自定义模板处理文本时,返回的是freemarker.template.Template对象。我希望获取原始模板对象以避免重复查询数据库,请问这在Freemarker中是否可行?


回答

当然可行!其实Freemarker的设计已经考虑到了这类场景,咱们一步步来解决你的问题:

1. 先修复当前代码里的重复查询坑

你现在的getLastModified方法每次都会调用templateRepository.findOne(template.getId())去数据库查一遍,这完全是没必要的冗余操作!因为findTemplateSource已经返回了最新的MyTemplate对象,你直接用这个对象的修改时间就好,不用再查库。修改后的代码应该是:

@Override
public long getLastModified(Object templateSource) {
    MyTemplate template = (MyTemplate) templateSource;
    // 直接使用传入的模板对象的修改时间,避免重复查询
    return template.getLastModifiedDate().toEpochMilli();
}

如果担心数据库里的模板被实时修改,你可以在findTemplateSource里加入缓存逻辑(比如用Guava Cache或者Spring Cache),设置合理的过期时间,或者监听数据库的变更事件来刷新缓存,这样既保证数据新鲜,又不会重复查库。

2. 获取原始的MyTemplate对象

Freemarker的Template对象本身就持有你在findTemplateSource中返回的templateSource实例!你只需要调用Template.getSource()方法就能直接拿到原始的MyTemplate对象,完全不需要再去数据库查询。示例代码:

// 你原本获取Freemarker Template的代码
Template freemarkerTemplate = contentFreemarkerConfig.getTemplate(
    CommunicationType.WALLET_BALANCE_THRESHOLD + "|" + Channel.EMAIL, 
    locale
);
// 直接获取原始MyTemplate对象
MyTemplate originalDbTemplate = (MyTemplate) freemarkerTemplate.getSource();
// 现在你可以直接用originalDbTemplate的subject、sender等属性,不用再查库

3. 额外的优化建议

  • Freemarker默认会缓存Template对象,只要模板的lastModified值没有变化,就不会重新调用findTemplateSource,这也能帮你减少数据库查询次数。
  • 如果你的模板修改不频繁,可以在findTemplateSource方法上加上Spring的@Cacheable注解,把查询到的MyTemplate缓存起来,进一步降低数据库压力。

内容的提问来源于stack exchange,提问作者drenda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:01:07