Template loading

Page Contents

Template loaders

模板加载器是根据抽象模板路径(例如"index.ftl""products/catalog.ftl")加载原始文本数据的对象。如果从何处以及如何加载模板“文件”,则取决于具体的模板加载器。它们可以是指定目录内的实际文件,也可以是数据库表中的值,或者是 Java Map 中的String -s 等。当您调用cfg.getTemplate(其中cfgConfiguration实例)时,FreeMarker 询问模板加载器(cfg.getTemplateLoader)返回给定模板路径的文本,然后 FreeMarker 将该文本解析为模板。它不在乎甚至不知道模板是否是真实文件,以及它在物理上的位置。这些细节仅由模板加载器知道。

内置模板加载器

您可以使用以下便捷方法在Configuration中设置三种最常见的模板加载机制:

  • void setDirectoryForTemplateLoading(File dir):在文件系统上设置要从中加载模板的目录。模板名称(模板路径)将相对于该物理目录进行解释。它不会让您在此目录之外加载文件。

  • void setClassForTemplateLoading(Class cl, String basePackagePath)void setClassLoaderForTemplateLoading(ClassLoader classLoader, String basePackagePath):这些是用于当您希望通过 Java 加载类的相同机制(从 Classpath 中模糊地说,从 Classpath)加载模板时使用的。这很可能是为生产代码加载模板的首选方式,因为它使您可以将所有内容保留在jar文件中。第一个参数决定将使用哪个 Java ClassLoader。第二个参数以/分隔的格式指定包含模板的包。请注意,如果不以/开头,它将相对于Class参数的包进行解释。

  • void setServletContextForTemplateLoading(Object servletContext, String path):采用基于 Servlet 的 Web 应用程序的上下文,以及一个基本路径,该路径相对于 Web 应用程序的根目录(是WEB-INF目录的父目录)进行解释。请注意,尽管此加载方法甚至适用于解压缩的.war文件,但由于这里使用ServletContext.getResource()来访问模板,因此我们在这里指的是“目录”。如果省略第二个参数(或使用""),则可以简单地存储与.ftl文件混合的静态文件(.html.jpg等)。当然,您必须为此在WEB-INF/web.xml中为*.ftl*.ftlh*.ftlx uri 模式设置一个 Servlet,否则 Client 端将按原样获得原始模板!为了避免发生此类事故,许多人更喜欢将模板存储在WEB-INF目录中的某个位置,该目录永远不能直接访问。这种机制很可能是为 Servlet 应用程序加载模板的首选方式,因为可以在不重新启动 Web 应用程序的情况下更新模板,而这通常不适用于类加载器机制。

如果您想使用自定义的TemplateLoader实现,或者需要设置内置模板加载器的一些其他设置,则需要自己实例化TemplateLoader对象,然后调用Configuration.setTemplateLoader(TemplateLoader)

WebappTemplateLoader templateLoader = new WebappTemplateLoader(servletContext, "WEB-INF/templates");
templateLoader.setURLConnectionUsesCaches(false);
templateLoader.setAttemptFileAccess(false);
cfg.setTemplateLoader(templateLoader);

从多个位置加载模板

如果需要从多个位置加载模板,则必须为每个位置实例化模板加载器对象,将它们包装到MultiTemplateLoader中,最后将该加载器传递给ConfigurationsetTemplateLoader(TemplateLoader loader)方法。这是一个使用两个不同的目录并使用 class-loader 加载模板的示例:

import freemarker.cache.*; // template loaders live in this package

...

FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates"));
FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates"));
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "/com/example/templates");

MultiTemplateLoader mtl = new MultiTemplateLoader(new TemplateLoader[] { ftl1, ftl2, ctl });

cfg.setTemplateLoader(mtl);

现在,FreeMarker 将尝试从/tmp/templates目录中加载模板,如果在该目录中找不到请求的模板,它将尝试从/usr/data/templates加载该模板,如果仍然找不到请求的模板,则它将尝试从_目录加载该模板。 com.example.templates Java 软件包。

从其他来源加载模板

如果没有任何内置的类加载器可以满足您的需求,则可以编写自己的实现freemarker.cache.TemplateLoader接口的类,并将其传递给ConfigurationsetTemplateLoader(TemplateLoader loader)方法。请阅读 API JavaDoc 了解更多信息。

如果您的模板源通过 URL 访问模板,则无需从头开始实现TemplateLoader;您可以选择继承freemarker.cache.URLTemplateLoader的子类,而仅实现URL getURL(String templateName)方法。

模板名称(模板路径)

由模板加载器决定如何解释模板名称(也称为模板路径)。但是,与其他组件一起使用时,路径格式受到限制。通常,强烈建议模板加载器使用 URL 样式的路径。路径不得使用/(路径步骤分隔符)字符,也不得使用.(相同目录)和..(父目录)路径步骤具有不同于 URL 路径(或 UN * X 路径)的含义。 *(星号)步骤也被保留,并用于 FreeMarker 的“模板获取”功能。

保留://(或将template_name_format设置设置为DEFAULT_2_4_0,将:(冒号)字符)保留为指定方案部分,这与 URI-s 相似。例如,someModule://foo/bar.ftl使用someModule,或者假设DEFAULT_2_4_0格式,classpath:foo/bar.ftl使用classpath方案。解释方案部分完全取决于TemplateLoader。 (FreeMarker 核心仅了解方案的概念,因为否则它将无法正确解析相对模板名称.)

FreeMarker 总是在将路径传递到TemplateLoader之前对其进行规范化,因此路径不包含/../或类似的路径,并且相对于虚构的模板根目录(即,它们不是以/开头)。它们也不包含*步骤,因为模板获取发生在较早的阶段。此外,通过将template_name_format设置设置为DEFAULT_2_4_0,多个连续的/ -s 将被归一化为单个/(除非它们是://方案分隔符的一部分)。

请注意,无论主机 os 如何,FreeMarker 模板路径都应始终使用斜杠(而不是反斜杠)。

Template caching

FreeMarker 缓存模板(假设您使用Configuration方法创建Template对象)。这意味着,当您调用getTemplate时,FreeMarker 不仅会返回生成的Template对象,而且会将其存储在缓存中,因此,当您下次使用相同(或等效)路径调用getTemplate时,它只会返回缓存的Template实例,并且无法加载并再次解析模板文件。

如果您更改模板文件,则 FreeMarker 将在下次获取模板时自动重新加载并重新解析模板。但是,由于始终检查更改对于处理大量模板的系统可能是负担,因此存在Configuration级设置,称为“更新延迟”(默认值为 5 秒)。自从上次检查较新版本以来已经过去了这么长时间,FreeMarker 不会再次检查模板是否已更改。如果要立即看到更改,请将此设置设置为 0.请注意,某些模板加载程序不会看到模板已更改,因为基础存储机制不支持该更改;例如,基于类加载器的模板加载器可能会出现此问题。

如果您调用getTemplate,将从缓存中删除一个模板,而 FreeMarker 意识到模板文件已被删除。另外,如果 JVM 认为它开始耗尽内存,则默认情况下,它可以从缓存中任意删除模板。此外,您可以使用ConfigurationclearTemplateCache方法手动清空缓存。您还可以使用removeTemplateFromCache从缓存中删除所选模板;无论“更新延迟”设置如何,都可以利用它来强制重新加载模板。

何时应丢弃缓存模板的实际策略是可以通过cache_storage设置插入的,通过该设置,您可以插入任何CacheStorage实现。对于大多数用户,freemarker.cache.MruCacheStorage就足够了。该缓存存储实现了两级“最近使用”缓存。在第一级中,对项目的引用最多达到指定的最大值(与软引用的项目相反,强引用的项目不能被 JVM 丢弃)。当超过最大值时,最近最少使用的项目将被移到第二级缓存中,在此对其进行软引用,直至达到另一个指定的最大值。坚固部分和柔软部分的大小可以使用构造函数指定。例如,将强 Component 的大小设置为 20,将软 Component 的大小设置为 250:

cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))

或者,由于MruCacheStorage是默认的缓存存储实现:

cfg.setSetting(Configuration.CACHE_STORAGE_KEY, "strong:20, soft:250");

当您创建一个新的Configuration对象时,它最初使用的是MruCacheStorage,其中strongSizeLimit是 0,softSizeLimitInteger.MAX_VALUE(实际上是无限的)。根据 JVM 的智能程度,使用非 0 strongSizeLimit可能是一个更安全的选择,因为只有软引用的项目,JVM 才会在资源短缺时抛出最常用的模板,然后必须重新加载和重新解析,使系统负担更大。