freemarker分析文档

来源:互联网 发布:算法的配套网站 编辑:程序博客网 时间:2024/04/25 22:26

FreeMarker是一个模板框架,它有自己的一套模板语言,整个框架的内核主要集中在两个包当中,其它包当中的类主要工具类或是扩充类,这两个核心的包是freemarker.corefreemarker.template

A.freemarker.core

这是框架的内核,它提供了框架的语言级支持,它所做的工作类似于ognl的主体工作,因为它两个工具都具有自己独立的语法,因为它们都需要构建自己的语法树,以及语法树中所种类型结点的定义,这个包的核心工具就是构建语法树,对整个框架提供了最底层的支持,如果没有它整个框架就不存在了。整个包的结构主要分为两大体系:一类是表达试(类似于java中的表达式);另一类是模板元素(类似于java中的关键字)它主要体现了一种语言的两个方面,最典型的两个方面。这两个类的关系是freemarker.core.Expression(以下简称Expression)与freemarker.core.TemplateElement(以下简称Element)它们有一个共同的基类就是freemarker.core.TemplateObjectExpression的主要任务是完成一个表达式的解析并将一个表达试与一个数据模型TemplateMode联系起来;Element主要任务是完成freemarker指令的解析,当然在指定解析的过程中需要有Expression的支持,因为通常一个指定都包含有相关的表达式。ExpressionElement代表了freemarker语法的两个重要方面,这两个方面相对独立,但又彼此联系,在这两个分支当中有几个需要注意的部分:

*        Expression内建语法(Build in):它是在一个freemarker表达式后跟一个?来表达这种语法的,在?之后跟了一个名称,这个名称是一个被注册过的内建表达式对象,这些对象都继承自freemarker.core.BuildIn类,而这个类又继承自Expression,一个BuildIn的作用就是将它之前的表达式进行修正和转换,比如之前的表达是一个字符串类型,我希望将它的第一个字母变成大写,这就是需要对前面表达式的结果进行修正转换,虽然这里为程序员提供了一个修正结果的接口,但是在一个模板页面上似乎没有提供注册的方式。

*        Element提供了两种扩充用户指令的方式:一种是通过页面宏的方式,另外一种是通过实现指定数据数据模型的方式即实现freemarker.template.TemplateDirectiveModel或者是freemarker.template.TemplateTransformModel(现在已经不被推荐使用)通过这两种方式都可以实现自己的方法库

 

B.freemarker.template

主要用来完成freemarker数据模型的抽象,freemarker框架采用的模式就是模板+数据=输出,而这里数据不是一般的数据它必须是实现了freemarker.template.TemplateModel的数据,此包主要就是构建这种数据模型的抽象体系结构。TemplateModel是框架内核采用的数据模型,尤其是在Expression中使用非常频繁,为什么要使用这样一种数据模型,而不是直接使用java即有的数据类型,我想主要考虑到框架的扩展性。在这个数据模型的抽象体系结构中主要有以下几种类型:

*        Scalars简单类型:主要有四种类型BooleanNumberStringDate,这四种java的基本类型经过处理后就变成了freemarker框架的四种基本类型

*        Containers容器类型:主要包括HashesSequencesCollections,这三种java的容器类型经过处理生就变成了freemarker框架的三种集合类型

*        Methods方法类型:这是有别于一般语言的一种类型,Expression中的可以含有方法调用,但它并不是一个真正意义下的调用,在内部这个方法调用将被解析成一种类型即方法类型,最终方法类型计算的结果是调用此类型规定接口方法所计算的结果

*        Directives指定类型:此类型也是freemarker比较特别的类型,这种类型将freemarker的指令与它的数据模型进行了联系,关于此类型的说明在上面已经论述过了,需要注意一点的是实现这种类型的时候方法一定要是无状态的一个指令类型对象可能同时被多个线程访问

*        Node variables结点类型:这种类型主要用来包装一个树上的结点,此种类型主要用在处理xml文档的时候特别有用

*        Object wrappers包装类型:这种类型并不是freemarker所使用的一种数据模型,而是用来产生freemarker所需要的数据类型一个包装类型,也就是说给它任何一个java中的数据类型,它都可以包装出freemarker中所需要的一种数据,所以在大多数据情况下我们并不需要直接提供一个TemplateModel对象,而是直接使用中的数据类型仍然可以freemarker内核进行沟通的原因就在于此

下面具体对freemarker的语法进行详细说明:

1          Freemarker的数据类型

在前面的讨论中已经对freemarker所采用的数据模板进行了分析,数据类型基本就是与它是对应的,这里再对这几种类型进行一次说明:

*        Scalars(基本型):StringNumberBooleanDate

*        Containers(容器):HashSequenceCollection

*        Subroutines(子程序):在前已经谈到了方法类型与指令类型,这里在对这种子程序类型进行一下深入的分析。在freemarker中的子程序类型实际上分得情况比较多,彼此之间有差别但又有联系,子程序主要分为以下几种:一种是Macro类型(具体还分为两种类型一类叫Macro另一类叫function)这种类型主要由模板页面中的<#Macro指令与<#function指令生成;一种是由实现了TemplateMethodModel接口对象叫做方法;一种是由实现TemplateDirectiveModel接口的对象叫做指令。如果将第一种类型分成两种类型第一类叫Macro;第二类叫Function;第三类叫Method;第四叫Directive。那这四类都可以称之为子程序类型,并且这四类有着不同寻常的关系,关系非常的具有美感。MacroFunction在模板中通过指令生成;MethodDirective通过在代码中实现接口生成;FunctionMethod属于Expression体系(只可以在表达式中进行调用);MacroDirective属于Element体系(只能以用户自定义指令的形式进行调用);从定义形式上讲MacroFunction可列为一组,而MethodDirective可列为另一组;从调用形式与效果上讲MacroDirective可列为组,而FunctionMethod可列为另一组;MacroDirective被统一成了一个UnifiedCall对象,而MethodFunction被统一成了一个MethodCall对象

*        Node(树结点类型):这种类型主要是为了处理xml文件而产生的

2          模板语法

Freemarker模板语法主要有指令、表达式、插值

*        指令:指令分为两种,一种是预定义指令,另一种是自定义指令。对于预定义指定一般开始标记为<#指令名称指令参数>,结束标记为</#指令名称>,如果对于一个预定义的指令没的标签体内容,则它没有结点标签,例如:<#include内容>,这里要特别注意,这个没有标签体的标签没有“/>”结束符,而是直接以“>”结束。关于这点它与自定义指令有一些差别,自定义指令以@代替#,但是如果没有标签体,自定义指令的写法应该为:<@mydirective something />

*        表达式:

n       指定直接值

u     Strings:"Foo" or 'Foo' or"It's /"quoted/"" orr"C:/raw/string"

u     Numbers:123.45

u     Booleans:true,false

u     Sequences:["foo", "bar", 123.45], 1..100

u     Hashes:{"name":"green mouse", "price":150}

n       获取变量值

u     Top-level variables:user

u     Retrieving data from a hash:user.name,user["name"]

u     Retrieving data from a sequence:products[5]

u     Special variable:.main

n       字符串操作

u     Interpolation (or concatenation):"Hello ${user}!" (or "Free" + "Marker")

u     Getting a character:name[0]

n       序列操作

u     Concatenation:users + ["guest"]

u     Sequence slice:products[10..19] or products[5..]

n       散列值操作

u     Concatenation:passwords + {"joe":"secret42"}

n       算术计算:(x * 1.5 + 10) / 2 - y % 100

n       比较:x == y,x != y,x < y,x > y,x >= y,x <= y,x &lt; y, ...etc.

n       逻辑操作:!registered && (firstVisit || fromEurope)

n       内建操作:name?upper_case

n       方法调用:repeat("What", 3)

n       缺失值控制操作:

u     默认值:name!"unknown" or (user.name)!"unknown" or name! or (user.name)!

u     缺失测试:name?? or (user.name)??

n       括号

n       表达式中的空白

n       操作符优先级

*        插值:${表达式},插值主要被使用在两个场合,一种是在文本区域中使用,例如<h1>hello${name};另一种是字符串字面量内嵌插值,例如<#include “/footer/${company}.html” >,一般第二种形式尽量少使用,因为它可以用字符串的加运算来取代,另外在指令中的表达式也要尽量避免使用插值形式,虽然可以使用,但一般意义不是很而且还容易出错