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

