On this page
Template loading
Page Contents
Template loaders
模板加载器是根据抽象模板路径(例如"index.ftl"
或"products/catalog.ftl"
)加载原始文本数据的对象。如果从何处以及如何加载模板“文件”,则取决于具体的模板加载器。它们可以是指定目录内的实际文件,也可以是数据库表中的值,或者是 Java Map 中的String
-s 等。当您调用cfg.getTemplate
(其中cfg
是Configuration
实例)时,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
文件中。第一个参数决定将使用哪个 JavaClassLoader
。第二个参数以/
分隔的格式指定包含模板的包。请注意,如果不以/
开头,它将相对于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
中,最后将该加载器传递给Configuration
的setTemplateLoader(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
接口的类,并将其传递给Configuration
的setTemplateLoader(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 认为它开始耗尽内存,则默认情况下,它可以从缓存中任意删除模板。此外,您可以使用Configuration
的clearTemplateCache
方法手动清空缓存。您还可以使用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,softSizeLimit
是Integer.MAX_VALUE
(实际上是无限的)。根据 JVM 的智能程度,使用非 0 strongSizeLimit
可能是一个更安全的选择,因为只有软引用的项目,JVM 才会在资源短缺时抛出最常用的模板,然后必须重新加载和重新解析,使系统负担更大。