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

Spring Boot中Freemarker多模板加载器回退功能失效问题

Freemarker MultiTemplateLoader 未触发回退加载问题

我在开发一个使用Freemarker作为模板引擎的Spring Boot项目,已配置MultiTemplateLoader从baselineUrl和dynamicUrl两个不同CDN地址加载模板,预期逻辑是当baselineUrl中找不到模板时自动回退到dynamicUrl加载。但实际运行时,baselineUrl返回403错误后,程序并未尝试从dynamicUrl加载,而该模板实际存在于dynamicUrl中。已验证两个URL均正确且可访问,也尝试清除缓存排除缓存问题,但回退功能仍未生效。

配置类代码

@Slf4j
@Configuration
@ConfigurationProperties("email")
@Data
public class FtlConfiguration {
  // https://dynamic/bhnpay-static/notification-template/ftl
  private String baselineUrl;
  // https://baseline/bhnpay-static/notification-template/ftl
  private String dynamicUrl;
  private long retentionTimeMillis;

  @Bean("emailConfiguration")
  public freemarker.template.Configuration createFtlConfigBean() throws IOException {
    freemarker.template.Configuration cfg = new freemarker.template.Configuration(
        freemarker.template.Configuration.VERSION_2_3_31);
    cfg.setDefaultEncoding(NotificationConstants.UTF_8);
    cfg.setLogTemplateExceptions(false);
    cfg.setWrapUncheckedExceptions(true);
    cfg.setFallbackOnNullLoopVariable(false);
    cfg.setLocalizedLookup(false);
    List<TemplateLoader> loaders = new ArrayList<>();
    // CloudTemplateLoader is just a wrapper on UrlTemplateLoader
    loaders.add(new CloudTemplateLoader(new URL(baselineUrl)));
    loaders.add(new CloudTemplateLoader(new URL(dynamicUrl)));
    cfg.setTemplateLoader(
        new MultiTemplateLoader(loaders.toArray(new TemplateLoader[0])));
    cfg.setCacheStorage(new MruCacheStorage(5000, Integer.MAX_VALUE));
    cfg.setTemplateUpdateDelayMilliseconds(retentionTimeMillis);
    return cfg;
  }

}

模板加载处理器代码

@Component
@Slf4j
public class FtlTemplateHandler {

    private final Configuration configuration;

    @Autowired
    public FtlTemplateHandler(@Qualifier("emailConfiguration") Configuration configuration) {
        this.configuration = configuration;
    }

    public String getTemplateAsHtml(String emailTemplateUrl) {
        try {
            Template template = configuration.getTemplate(emailTemplateUrl);
            // Process the template
        } catch (IOException e) {
           try {
             log.info("removing template from cache {}", emailTemplateUrl);
             configuration.removeTemplateFromCache(emailTemplateUrl);
             Thread.sleep(10000);
           } catch (IOException | InterruptedException ex) {
             // process exception
           }
       }
    }
}

CloudTemplateLoader实现代码

@Slf4j
public class CloudTemplateLoader extends URLTemplateLoader {
  private URL root;
  public CloudTemplateLoader(URL root) {
    super();
    this.root = root;
  }
  public URL getRoot() {
    return root;
  }
  public void setRoot(URL root) {
    this.root = root;
  }
  @Override
  protected URL getURL(String template) {
    try {
      return new URL(root, template);
    } catch (MalformedURLException e) {
      log.error("Error while loading templates from cloud.");
    }
    return null;
  }
}

错误日志

Caused by: java.io.IOException: Server returned HTTP response code: 403 for URL: https://baseline/bhnpay-static/notification-template/ftl/en-US/b9b09aa4-fd39-4812-924e-1fe7f6d4ed51.ftl
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:2000)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getHeaderField(HttpURLConnection.java:3256)
    at java.base/java.net.HttpURLConnection.getHeaderFieldDate(HttpURLConnection.java:601)
    at java.base/java.net.URLConnection.getLastModified(URLConnection.java:567)
    at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getLastModified(HttpsURLConnectionImpl.java:392)
    at freemarker.cache.URLTemplateSource.lastModified(URLTemplateSource.java:94)
    at freemarker.cache.URLTemplateLoader.getLastModified(URLTemplateLoader.java:50)
    at freemarker.cache.MultiTemplateLoader$MultiSource.getLastModified(MultiTemplateLoader.java:142)
    at freemarker.cache.MultiTemplateLoader.getLastModified(MultiTemplateLoader.java:99)
    at freemarker.cache.TemplateCache.getTemplateInternal(TemplateCache.java:439)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:14:51