Freemarker深入解析
一:创建一个Configuration实例
// Where the application is initialized; in general you do this ONLY ONCE in the application life-cycle!
Configuration cfg = new Configuration();
cfg.setSomeSetting(...);
cfg.setOtherSetting(...);
...
// Later, whenever the application needs a template (so you may do this a lot, and from multiple threads):
Template myTemplate = cfg.getTemplate("myTemplate.html");
myTemplate.process(dataModel, out);
上面是摘自freemarker.template.Configuration类的注释,介绍了这个类的使用方法。(本文的freemarker版本为2.3.20)
这个类是进入freemarker API的主要入口类。
从注释可以知道这个类主要的功能:封装了freemarker的配置设置,并可用于模板加载,提供缓存(模板)的能力。
下面从代码里来一一看看是如何实现这些功能的。
public Configuration() {
cache = new TemplateCache();
cache.setConfiguration(this);
cache.setDelay(5000);
loadBuiltInSharedVariables();
}
可以看到,默认构造器中涉及一个缓存行为,原来上面说的缓存能力是这个TemplateCache类里面写的。先不用管TemplateCache类的细节。先将这个构造流程看一遍。
cache.setConfiguration(this);
这一句将当前这个Configuration类通过setConfiguration方法注入到TemplateCache这个类中。
loadBuiltInSharedVariables方法加载了freemarker内建的配置,包括xml、html格式的文件的特殊字符转义工具类,还有空格等的处理。
构造了Configuration的实例后,要做一些自定义配置,比如设置编码
configuration.setDefaultEncoding("utf-8");
二:创建数据模型
这里是按照官方文档顺序,非入门可跳过直接看三:获取模板。
可以使用java.lang和java.util包中的类,还可以自定义Java Bean类型来构建数据对象。
三:获取模板
设置模板加载路径
configuration.setClassForTemplateLoading(this.getClass(), "/template");
上面的方法是告诉freemarker模板加载路径,具体看代码:
public void setClassForTemplateLoading(Class clazz, String pathPrefix) {
setTemplateLoader(new ClassTemplateLoader(clazz, pathPrefix));
}
ClassTemplateLoader用于保存传入的参数,构造一个可以确定模板路径的对象,freemarker可以调用其父类URLTemplateLoader的findTemplateSource方法获取模板在运行时的路径。
Freemarker将模板加载的工作抽象为TemplateLoader接口:
ClassTemplateLoader是TemplateLoader接口的一种实现,查找模板路径的方法一般定义在findTemplateSource方法中,然而ClassTemplateLoader本身没有复写findTemplateSource方法,而是使用父类URLTemplateLoader的findTemplateSource方法,URLTemplateLoader的findTemplateSource方法又需要使用到ClassTemplateLoader的getURL方法,所以ClassTemplateLoader的模板加载是在getURL方法中完成的。
URLTemplateLoader的findTemplateSource方法:
public Object findTemplateSource(String name)
throws
IOException
{
URL url = getURL(name);
return url == null ? null : new URLTemplateSource(url);
}
使用构造ClassTemplateLoader传入的path和后面将要设置的模板名称name拼接出模板全路径,然后使用类加载器的方式去查找资源,返回运行时的统一资源定位符URL。
protected URL getURL(String name)
{
String fullPath = path + name;
// Block java.net.URLClassLoader exploits:
if (path.equals("/") && !isSchemeless(fullPath)) {
return null;
}
return loaderClass.getResource(fullPath);
}
通过查看setTemplateLoader方法的注释文档可以知道,官方支持三种方式设置模板加载路径:
1. 通过 Class.getResource():setClassForTemplateLoading(Class, String);
2. 通过设置具体目录:setDirectoryForTemplateLoading(File)
3. 如果是web项目,也可以通过ServletContext加载:setServletContextForTemplateLoading(Object, String)
TemplateLoader接口层次:
由上图可知,TemplateLoader还有多种实现,即其他的获取模板路径的方式。后面会再写一篇Spring集成freemarker,是如何加载freemarker模板的。
接着看:
public synchronized void setTemplateLoader(TemplateLoader loader) {
createTemplateCache(loader, cache.getCacheStorage());
}
setTemplateLoader中调用createTemplateCache方法,可以想象,这里涉及缓存模板的逻辑了。
cache.getCacheStorage(),返回一个CacheStorage实例,CacheStorage是真正缓存模板的,它有几种实现:
参考:http://freemarker.foofun.cn/pgui_quickstart_createdatamodel.html