[关闭]
@Catyee 2017-05-26T01:00:38.000000Z 字数 6486 阅读 606

Java工具类——FreeMarker

java FreeMarker


FreeMaker是一个模板引擎,它基于模板和数据来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)。也就是说它可以生成文件,而这些文件的格式是我们指定好的格式。文件中的内容是通过插值来填充的。也就是所谓的模板+数据=输出。

首先来看看FreeMarker模板文件,它是以.ftl为后缀的文件,主要由如下4个部分组成:
1. 文本:直接输出的部分
2. 注释:<#-- ... -->格式部分,不会输出
3. 插值:即${...}或#{...}格式的部分,将使用数据模型中的部分替代输出
4. FTL指令:FreeMarker指定,和HTML标记类似,名字前加#予以区分,不会输出

具体编程的话,在Java中,为了使用FreeMarker将数据模型中的值合并到从模板文件生成的模板中,可按如下步骤进行:
1. 创建配置实例:首先,创建一个Configuration的实例,然后调整它的设置。Configuration实例是存储FreeMarker应用级设置的核心部分。同时,它也处理那些创建和缓存预解析模板的工作。

  1. Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
  2. // 指定模板文件从何处加载的数据源,这里设置成一个文件目录。
  3. cfg.setDirectoryForTemplateLoading(new File("/where/you/store/templates"));
  4. // 指定模板如何检索数据模型,后面会讲到,先可以这么来用:
  5. cfg.setObjectWrapper(new DefaultObjectWrapper());
  6. //指定异常处理句柄
  7. cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
  8. //指定默认的编码格式
  9. cfg.setDefaultEncoding("UTF-8");

这些工作只在应用(可能是servlet)生命周期的开始执行一次。

  1. 创建数据模型
    比如这样一个数据模型:
    (root)
    |
    +- user = "Big Joe"
    |
    +- latest Product
      |
      +- url = "products/greenmouse.html"
      |
      +- name = "green mouse"
    用java来构建:
  1. // 创建根哈希表
  2. Map root = new HashMap();
  3. // 在根中放入字符串"user"
  4. root.put("user", "Big Joe");
  5. // 为"latestProduct"创建哈希表
  6. Map latest = new HashMap();
  7. // 将它添加到根哈希表中
  8. root.put("latestProduct", latest);
  9. // 在 latest 中放置"url"和"name"
  10. latest.put("url", "products/greenmouse.html");
  11. latest.put("name", "green mouse");

现在root就是要填充到模板template中的数据了。

7.获得模板
现在有一个模板文件:test.ftl,存放在之前设置的目录中。test.ftl文件中的内容(即模板)如下:

  1. <html>
  2. <head>
  3. <title>Welcome!</title>
  4. </head>
  5. <body>
  6. <h1>Welcome ${user}!</h1>
  7. <p>Our latestproduct:</p>
  8. <a href="${latestProduct.url}">${latestProduct.name}</a>!
  9. </body>
  10. </html>

在程序中我们可以用freemarker.template.Template来临时存储模板的格式,当我们需要一个模板实例的时候,就可以使用Configuration的getTemplate方法来获取。在之前设置的目录中,我们用test.ftl存储了示例模板,那么就可以这样来做:

  1. Template temp = cfg.getTemplate("test.ftl");

当调用这个方法的时候,将会创建一个Template实例,它是test.ftl文件经过读取和解析(编译)之后创建的模板实例。Template实例以解析后的形式存储模板,而不是以源文件的文本形式。
7. 合并模板和数据模型
我们都已经知道:数据模型+模板=输出,现在我们已经有了一个数据模型root和一个模板temp了,所以为了得到输出就需要合并它们。这是由模板的process方法完成的。它用数据模型root和Writer对象作为参数,然后向
Writer对象写入产生的内容。为简单起见,这里我们只做标准的输出:

  1. Writer out = new OutputStreamWriter(System.out);
  2. temp.process(root, out);
  3. out.flush();

上面的代码会向你的终端以数据模板的格式输出数据模型的数据。
完整的代码:

  1. import freemarker.template.*;
  2. import java.util.*;
  3. import java.io.*;
  4. public classTest {
  5. public static void main(String[] args) throws Exception {
  6. /* 在整个应用的生命周期中,这个工作你应该只做一次。 */
  7. /* 创建和调整配置。 */
  8. Configuration cfg = new Configuration();
  9. cfg.setDirectoryForTemplateLoading(new File("/where/you/store/templates"));
  10. cfg.setObjectWrapper(new DefaultObjectWrapper());
  11. /* 在整个应用的生命周期中,这个工作你可以执行多次 */
  12. /* 获取或创建模板*/
  13. Template temp = cfg.getTemplate("test.ftl");
  14. /* 创建数据模型 */
  15. Map root = new HashMap();
  16. root.put("user", "Big Joe");
  17. Map latest = new HashMap();
  18. root.put("latestProduct", latest);
  19. latest.put("url", "products/greenmouse.html");
  20. latest.put("name", "green mouse");
  21. /* 将模板和数据模型合并 */
  22. Writer out = new OutputStreamWriter(System.out);
  23. temp.process(root, out);
  24. out.flush();
  25. }
  26. }

上面已经讲了基本用法,现在进行更深入的理解:

Configuration

Configuration存储了常用(全局,应用程序级)的设置,定义了想要在所有模板中可用的变量(称为共享变量)。 而且,它会处理 Template 实例的新建和缓存。通常使用一个共享的单实例Configuration对象。

设置共享变量,是为所有模板所定义的变量。

  1. cfg.setSharedVariable(String name,Object obj);

比如:

  1. Configuration cfg = new Configuration();
  2. ...
  3. cfg.setSahredVariable("wrap", new WrapDirective());
  4. // 使用 ObjectWrapper.DEFAULT_WRAPPER
  5. cfg.setSharedVariable("company", "Foo Inc.");

上面这段代码表示:
在所有使用这个配置的模板中,名为wrap的用户自定义指令。
一个名为company的字符串将会在数据模型的根上可见,这样就不用在根哈希表上一次又一次的添加它们。在传递给Template.process根对象里的变量将会隐藏同名的共享变量。
警告:如果配置对象在多线程环境中使用,不要使用TemplateModel实现类来作为共享变量,因为它是线程不安全的。这也是基于Servlet的Web站点的典型情况。

配置设置
配置设置有很多, 例如:locale,number_format,default_encoding, template_exception_handler。
比如说设置编码格式:

  1. cfg.setDefaultEncoding("UTF-8");

那么使用该配置的所有模板的编码格式都是UTF-8。
配置信息可以被想象成3层(Configuration, Template,Environment),最高层包含特定的值,它为设置信息提供最有效的值。 比如(设置信息A到F仅仅是为这个示例而构想的):

Setting A Setting B Setting C Setting D Setting E Setting F
Layer 3: Environment 1 - - 1 - -
Layer 2: Template 2 2 - - 2 -
Layer 1: Configuration 3 3 3 3 - -

配置信息的有效值为:A=1,B=2,C=3,D=1,E=2。 而F的设置则是null,或者在你获取它的时候将抛出异常。

  1. Configuration myCfg = new Configuration(Configuration.VERSION_2_3_23);
  2. myCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
  3. myCfg.setDefaultEncoding("UTF-8");

在真正使用 Configuration 对象 (通常在初始化应用程序时)之前来配置它,后面必须将其视为只读的对象。

  1. Environment env = myTemplate.createProcessingEnvironment(root, out);
  2. env.setLocale(java.util.Locale.ITALY);
  3. env.setNumberFormat("0.####");
  4. env.process(); // process the template

二是在模板中直接使用setting指令,但是这种方法并不推荐,例如:

  1. <#setting locale="it_IT">
  2. <#setting number_format="0.####">

在Environment层中,什么时候改变配置信息是没有限制的。

模板加载
模板加载器加载基于抽象模板路径下的比如 "index.ftl" 或 "products/catalog.ftl"的原生文本数据对象。加载完毕之后,它会解析文本来生成模板。

  1. void setDirectoryForTemplateLoading(File dir);
  1. void setClassForTemplateLoading(Class cl, String prefix);
  1. void setServletContextForTemplateLoading(Object servletContext, String path);

上述的第一种方法在磁盘的文件系统上设置了一个明确的目录, 它确定了从哪里加载模板。
第二种调用方法的参数是一个类和一个路径前缀,参数prefix是给模板的名称来加前缀的,其实就是文件相对于给定类的路径。这个方法顾名思义就是通过获得给定类的相对位置以及之后的prefix参数来获得template文件。这样方便我们将template文件一起打成jar包使用。经验之谈,这里prefix必须以“/”开头。
第三种调用方式需要Web应用的上下文和一个基路径作为参数,这个基路径是Web应用根路径(WEB-INF目录的上级目录)的相对路径。加载器会从Web应用目录开始加载模板。而且加载方法对没有打包的.war文件也起作用,因为它使用了ServletContext.getResource()方法来访问模板。比如: setServletContextForTemplateLoading(context, "/ftl") 就是 /WebRoot/ftl目录。

  1. FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/temp/templates"));
  2. FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/user/data/templates"));
  3. ClassTemplateLoader ctl1 = new ClassTemplateLoader(TemplateLoaderTest.class, "/");
  4. TemplateLoader[] loaders = new TemplateLoader[]{ftl1, ftl2, ctl1};
  5. MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);
  6. cfg.setTemplateLoader(mtl);

现在,FreeMarker将会尝试从 /tmp/templates目录加载模板,如果在这个目录下没有发现请求的模板,它就会继续尝试从/usr/data/templates 目录下加载,如果还是没有发现请求的模板, 那么它就会使用类加载器来加载模板。

完整的FreeMaker学习资料:FreeMarker_2.3.23_Manual_zh_CN

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注