freemarker代码生成器

freemarker数据模型
数据模型可以被看成是树形结构。
标量用于存储单一的值。这种类型的值可以是字符串,数字,日期/时间或者是布尔值。
哈希表是一种存储变量及其相关且有唯一标识名称的容器。
序列是存储有序变量的容器。存储的变量可以通过数字索引来检索,索引通常从0开始。C](这里写自定义目录标题)

if 指令
使用 if 指令可以有条件地跳过模板的一些片段。 比如,假设在 最初的示例 中, 想向你的老板Big Joe特别地问好,可其他人不同
通常来讲,如果 condition 是false(布尔值),那么介于 <#if condition> 和 </#if> 标签中的内容会被略过。

condition 的使用: == 是用来判断它两侧的值是否相等的操作符, 比较的结果是布尔值,也就是true或者false。在 == 的左侧,是 被引用的变量, 我们很熟悉这样的语法结构;最终它会被变量的值所替代。通常来说, 在指令或插值中没有被引号标注的内容都被视为变量的引用。右侧则是指定的字符串, 在模板中的字符串 只能 放在引号内。

当价格为0时,就会打印出 “Pythons are free today!”:
<#if animals.python.price == 0>
Pythons are free today!
</#if>

创建 Configuration 实例
,应该使用 单 实例配置(也就是说,它是单例的)。 请注意不管一个系统有多少独立的组件来使用 FreeMarker, 它们都会使用他们自己私有的 Configuration 实例。

默认对象包装器
object_wrapper Configuration 的默认设置是 freemarker.template.DefaultObjectWrapper 实例。除非有特别的需求,那么建议使用这个对象包装器,或者是自定义的 DefaultObjectWrapper 的子类。

它会识别大部分基本的Java类型,比如 String, Number,Boolean, Date,List (通常还有全部的 java.util.Collection 类型), 数组,Map等。并把它们自然地包装成匹配 TemplateModel 接口的对象。它也会使用 freemarker.ext.dom.NodeModel 来包装W3C DOM结点, 所以可以很方便地处理XML, 在XML章节会有描述)。 对于Jython对象,会代理到 freemarker.ext.jython.JythonWrapper 上。 而对于其它所有对象,则会调用 BeansWrapper.wrap(超类的方法), 暴露出对象的JavaBean属性作为哈希表项 (比如FTL中的 myObj.foo 会在后面调用 getFoo()), 也会暴露出对象(比如FTL中的 myObj.bar(1, 2) 就会调用方法) 的公有方法(JavaBean action)

多线程
在多线程运行环境中, Configuration 实例, Template 实例和数据模型应该是永远不能改变(只读)的对象。 也就是说,创建和初始化它们(如使用 set… 方法)之后,就不能再修改它们了(比如不能再次调用 set… 方法)。 这就允许我们在多线程环境中避免代价很大的同步锁问题。要小心 Template 实例; 当使用了 Configuration.getTemplate 方法获得 Template 一个实例时,也许得到的是从模板缓存中缓存的实例, 这些实例都已经被其他线程使用了,所以不要调用它们的 set… 方法 (当然调用 process 方法还是不错的)。
如果只从 同一个 独立线程中访问所有对象, 那么上面所述的限制将不会起作用

方法
方法变量在存于实现了 TemplateMethodModel 接口的模板中。这个接口包含一个方法: TemplateModel exec(java.util.List arguments)。 当使用 方法调用表达式 调用方法时,exec 方法将会被调用。 形参将会包含FTL方法调用形参的值。exec 方法的返回值给出了FTL方法调用表达式的返回值。
TemplateMethodModelEx 接口扩展了 TemplateMethodModel 接口。它没有添加任何新方法。 事实上这个对象实现这个 标记 接口是给FTL引擎暗示, 形式参数应该直接以 TemplateModel 的形式放进 java.util.List。否则将会以 String 形式放入list。

结点变量
结点变量体现了树形结构中的结点。结点变量的引入是为了帮助用户 在数据模型中处理XML文档, 但是它们也可以用于构建树状模型。如需要有关从模板语言角度考虑的结点信息, 那么可以 阅读之前章节。
结点变量有下列属性,它们都由 TemplateNodeModel 接口的方法提供:

基本属性:
TemplateSequenceModel getChildNodes(): 一个结点的子结点序列(除非这个结点是叶子结点,这时方法返回一个空序列或者是null)。 子结点本身应该也是结点变量。
TemplateNodeModel getParentNode(): 一个结点只有一个父结点(除非这个结点是结点树的根结点, 这时方法返回null)。
可选属性。如果一个属性在具体的使用中没有意义, 那对应的方法应该返回null:
String getNodeName(): 结点名称也是宏的名称,当使用 recurse 和 visit指令时, 它用来控制结点。因此,如果想通过结点使用这些指令, 那么结点的名称是 必须的。
String getNodeType():在XML中: “element”,“text”, "comment"等。如果这些信息可用, 就是通过 recurse 和 visit 指令来查找结点的默认处理宏。而且,它对其他有具体用途的应用程序也是有用的。
String getNamespaceURI(): 这个结点所属的命名空间(和用于库的FTL命名空间无关)。例如,在XML中, 这就是元素和属性所属XML命名空间的URI。这个信息如果可用,就是通过 recurse 和 visit 指令来查找存储控制宏的FTL命名空间。
在FTL里,结点属性的直接使用可以通过 内建函数 node完成, 还有 visit 和 recurse 宏。
在很多用例中,实现了 TemplateNodeModel 接口和其它接口的变量,因为结点变量属性仅仅提供基本的结点间导航的方法。

Warning!
不需要重复创建 Configuration 实例; 它的代价很高,尤其是会丢失缓存。Configuration 实例就是应用级别的单例。

当使用多线程应用程序(比如Web网站),Configuration 实例中的设置就不能被修改。它们可以被视作为 “有效的不可改变的” 对象, 也可以继续使用 安全发布 技术 (参考 JSR 133 和相关的文献)来保证实例对其它线程也可用。比如, 通过final或volatile字段来声明实例,或者通过线程安全的IoC容器,但不能作为普通字段。 (Configuration 中不处理修改设置的方法是线程安全的。)

标量:字符串 数字 布尔值 日期/时间
日期:日期变量可以存储和日期/时间相关的数据。 一共有三种变化:
日期:精确到天的日期,没有时间部分。比如April 4, 2003。
时间:精确到毫秒,没有日期部分。比如10:19:18 PM。
日期-时间(有时也被称为"时间戳"),比如April 4,2003 10:19:18 PM。 有日期和时间两部分,时间部分的存储精确到毫秒。

容器:哈希表 序列 集合

分析源代码之后发现struts2的FreemarkerResult在解析FTL文件的时候依据Configuration的属性去推断其属性TemplateExceptionHandler是否为"RETHROW_HANDLER"。也就是说是否抛出这个异常,假设抛出则因为FremarkerResult的这些代码是由struts2操作的(大家能够看源代码,事实上上终于是由DefaultActionInvocation的executeResult来调用的,这个类是贯穿拦截器、action、result的一个核心调动类。详细的这里我们不做讨论)。所以假设一旦Freemarker抛出异常,则这个异常会被struts2的全局异常处理来解决。否则就会由freemarker自己解决,

合并模板和数据模型
我们已经知道,数据模型+模板=输出,我们有了一个数据模型 (root) 和一个模板 (temp), 为了得到输出就需要合并它们。这是由模板的 process 方法完成的。它用数据模型root和 Writer 对象作为参数,然后向 Writer 对象写入产生的内容。 为简单起见,这里我们只做标准的输出:
Writer out = new OutputStreamWriter(System.out);
temp.process(root, out);

共享变量
Shared variables (共享变量)是为所有模板定义的变量。可以使用 setSharedVariable 方法向配置中添加共享变量:
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);

cfg.setSharedVariable(“warp”, new WarpDirective());
cfg.setSharedVariable(“company”, “Foo Inc.”);

配置信息可以被想象成3层(Configuration, Template,Environment), 最高层包含特定的值,它为设置信息提供最有效的值

Configuration 层: 原则上设置配置信息时使用 Configuration 对象的setter方法,例如:
Configuration myCfg = new Configuration(Configuration.VERSION_2_3_23);
myCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
myCfg.setDefaultEncoding(“UTF-8”);
在真正使用 Configuration 对象 (通常在初始化应用程序时)之前来配置它,后面必须将其视为只读的对象。
在实践中,比如很多Web应用框架中,就应该使用这种框架特定的配置方式来进行配置, 比如使用成对的 String 来配置(像在 .properties 属性配置文件中那样)。 在这种情况下,框架的作者大多数使用 Configuration 对象的 setSetting(String name, String value) 方法。 这可以参考setSetting的API文档 部分来获取可用的设置名和参数的格式的信息。 而在Spring框架中,我们可以这样进行:



2.3.23
rethrow
UTF-8



请注意,这种形式的配置( String 键-值对) 和直接使用 Configuration 的API相比, 很不幸地被限制了。

**Template 层:**对于被请求的本地化信息,模板的 locale 设置由 Configuration.getTemplate(…) 来设置。 否则,就不能在这里进行设置,除非想控制 Template 对象来代替 freemarker.cache.TemplateCache,这样的话, 应该在 Template 对象第一次被使用前就设置配置信息, 然后就将 Template 对象视为是只读的

Environment 层:这里有两种配置方法:
使用Java API:使用 Environment 对象的setter方法。当然想要在模板执行之前来做,然后当调用 myTemplate.process(…) 时会遇到问题,
因为在内部创建 Environment 对象后立即就执行模板了, 导致没有机会来进行设置
这个问题的解决可以用下面两个步骤进行:

Environment env = myTemplate.createProcessingEnvironment(root, out);
env.setLocale(java.util.Locale.ITALY);
env.setNumberFormat(“0.####”);
env.process(); // process the template
在模板中(通常这被认为是不好的做法)直接使用 setting 指令,例如:
<#setting locale=“it_IT”>
<#setting number_format=“0.####”>
在这层,当什么时候改变配置信息,是没有限制的。

要知道 FreeMarker 支持什么样的配置信息还有它们的意义, 可以先看看FreeMarker Java API文档中的下面这部分内容:
在三层中 freemarker.core.Configurable 的setter方法来配置。
只在 Configuration 层可用的 freemarker.template.Configuration 的setter方法来配置。
在三层中可用 String 键-值对书写的 freemarker.core.Configurable.setSetting(String,String) 配置。
只在 Configuration 层中可用 String 键-值对书写的 freemarker.template.Configuration.setSetting(String, String) 配置。

模板加载器
模板加载器是加载基于抽象模板路径下,比如 “index.ftl” 或 “products/catalog.ftl” 的原生文本数据对象。 这由具体的模板加载器对象来确定它们取得请求数据时使用了什么样的数据来源 (文件夹中的文件,数据等等)。当调用 cfg.getTemplate (这里的 cfg 就是 Configuration 实例)时, FreeMarker询问模板加载器是否已经为 cfg 建立返回给定模板路径的文本,之后 FreeMarker 解析文本生成模板。

内建模板加载器
在 Configuration 中可以使用下面的方法来方便建立三种模板加载。 (每种方法都会在其内部新建一个模板加载器对象,然后创建 Configuration 实例来使用它。)
void setDirectoryForTemplateLoading(File dir); 根据文件路径加载

void setClassForTemplateLoading(Class cl, String prefix); Class文件加载

void setServletContextForTemplateLoading(Object servletContext, String path); web应用的上下文
上述的第一种方法在磁盘的文件系统上设置了一个明确的目录, 它确定了从哪里加载 模板。不要说可能,File 参数肯定是一个存在的目录。否则,将会抛出异常。
第二种调用方法使用了一个 Class 类型的参数和一个前缀。这是让你来指定什么时候通过相同的机制来加载模板, 不过是用Java的 ClassLoader 来加载类。 这就意味着传入的class参数会被 Class.getResource() 用来调用方法来找到模板。参数 prefix 是给模板的名称来加前缀的。在实际运行的环境中, 类加载机制是首选用来加载模板的方法,通常情况下,从类路径下加载文件的这种机制, 要比从文件系统的特定目录位置加载安全而且简单。在最终的应用程序中, 所有代码都使用 .jar 文件打包也是不错的, 这样用户就可以直接执行包含所有资源的 .jar 文件了。
第三种调用方式需要Web应用的上下文和一个基路径作为参数, 这个基路径是Web应用根路径(WEB-INF目录的上级目录)的相对路径。 那么加载器将会从Web应用目录开始加载模板。尽管加载方法对没有打包的 .war 文件起作用,因为它使用了 ServletContext.getResource() 方法来访问模板, 注意这里我们指的是“目录”。如果忽略了第二个参数(或使用了""), 那么就可以混合存储静态文件(.html,.jpg等) 和 .ftl 文件,只是 .ftl 文件可以被送到客户端执行。 当然必须在 WEB-INF/web.xml 中配置一个Servlet来处理URI格式为 *.ftl 的用户请求,否则客户端无法获取到模板, 因此你将会看到Web服务器给出的秘密提示内容。在站点中不能使用空路径,这是一个问题, 你应该在 WEB-INF 目录下的某个位置存储模板文件, 这样模板源文件就不会偶然地被执行到,这种机制对servlet应用程序来加载模板来说, 是非常好用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值