httl页面静态化模板帮助文档

更新时间:2023-12-19 01:43:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

1.概述

HTTL(Hyper-Text Template Language)是一个高性能的JAVA开源模板引擎,适用于动态HTML页面输出,可替代JSP页面,它的指令和Velocity相似。

快速

将模板编译成JAVA字节码运行,并使用强类型推导,减少运行期反射和转型,渲染速度是Velocity,Freemarker等其它模板引擎的10倍,请参见第3章的性能对比。注:JSP只有Scriptlet是编译的,Tag和EL是解释执行的,而HTTL是全编译的。

简洁

保持最简洁指令集,只保留基本的占位/注释/转义,和八个必需的控制指令,其它都降级为表达式方法实现,比如:$!{include(\。

直觉

语法尽可能符合HTML和JAVA开发者的直觉,指令类似于常用Velocity指令,但改进了Velocity中不符合直觉的地方,请参见第5章与Velocity的对比。

友好

模板自描述,在模板内声明入参变量类型,并基于入参类型推导模板内所有变量类型,使IDE能方便的实现变量方法补全提示,对开发过程友好。

2.示例

2.1 调用示例

BooksServlet.java:

import httl.*;

import java.util.*;

Map parameters = new HashMap(); parameters.put(\parameters.put(\

Engine engine = Engine.getEngine();

Template template = engine.getTemplate(\template.render(parameters, response.getWriter()); 注:缺省配置下,HTTL不依赖任何三方库,只需JDK1.5+即可。

2.2 模板示例

books.httl:

${book.title}

2.3 配置示例

httl.properties:

import.packages+=com.xxx

template.directory=META-INF/templates input.encoding=UTF-8 output.encoding=UTF-8

其中,+=表示追加配置,而不覆盖缺省配置。注意,所有配置项都有缺省值,如果使用缺省值,可以不配,更多配置参见文档第7章。

3.性能

3.1 性能对比

性能测试类:PerformanceTest.java 引擎 javahttl

模板

初始化 编译 首渲染 输出大小 十万渲染 每秒次数

1ms 0ms

28,778byte 8,739ms 11,442/s 28,778byte 9,608ms 10,407/s

books.java0ms

books.httl88ms 621ms 3ms

velocitysmarty4jbeetl

books.vm

21ms 96ms 23ms 28,172byte 41,969ms 2,382/s

freemarkerbooks.ftl103ms 121ms 141ms 27,571byte 56,192ms 1,779/s

books.st

2ms

75ms 18ms 29,044byte 65,855ms 1,518/s

books.btl24ms 11ms 159ms 26,820byte 81,428ms 1,228/s

环境:os: Mac OS X 10.8.2, cpu: 2 x 1.70GHz, mem: 4G, jvm: 1.7.0_09 ->

mem: 80M

HTTL的渲染速度接近于直接用Java硬编码输出,比其它模板引擎高出10倍。

HTTL用到的JDK的Compiler,编译一个类通常需要几百毫秒,比其它模板的编译要慢,但每个模板只在加载时编译一次。

注:不同环境的运行结果可能存在差异,以上测试数据仅供参考,你可以在自己的机器上执行测试:

git clone https://github.com/httl/httl.git cd httl

mvn test -Dtest=httl.test.PerformanceTest -Dstream=false -Dsize=200 -Dcount=10000

另外,HTTL缺省开启了EscapeXmlFilter,而其它模板引擎没有,为了公平,性能测试时配置关闭了Filter: value.filters=null

3.2 优化策略

3.2.1 强类型编译,并推导关联类型

对于表达式${user.name}的编译: 弱类型字节码生成:

Object user = context.get(\无法确定user是Map还是POJO // 反射获取属性的值,而且要运行期判断是user.getName(),还是user.name字段

Object name = ReflectUtil.get(user, \接下来name也要反射 HTTL强类型字节码生成:

User user = (User)context.get(\通过in=\声明类型 // 编译期通过User的字段类型推演name的类型,并在编译期决定使用getName()

String name = user.getName();

3.2.2 编译时就将文本编译成字节,加快输出

文本编译:

writer.write(\writer.write(user.getName()); 二进制编译:

output.write(new byte[] {60, 116, 97, 98, 108, 101, 62, 60, 116, 114, 62, 60, 116, 100, 62});

output.write(user.getName().getBytes());

这样可以大幅度降低IO输出时将文本通过字符集编码成二进制流的速度。 HTTL缺省每模板同时生成两份class,在用户传入OutputStream和Writer时,执行不同的class:

template.render(paramaters, outputStream); // 内部将执行二进制输出版本的Template类

template.render(paramaters, writer); // 内部将执行文本输出版本的Template类

3.2.3 对于赋值生成局部变量,不put回参数map

比如将:

${price} 编译成:

int price = price * discount / 100; write(price); 而不是:

context.put(\write(context.get(\

这样可以大量减少参数map的put和get的调用。

3.2.4 将文本不编译到字节码中,减少内存perm区占用,以及防止JIT失效:

当模板的内容较大时,会导致生成的字节码也比较大,字节码运行时会放在内存perm区,导致perm区过大。模板多时,用户可能需要不断调大perm区:java -XX:PermSize=256MB -XX:MaxPermSize=256MB

另外,SunJDK缺省对大于8K字节码的方法不进行JIT优化,我们常规开启的JVM是mixed模式的,即调用量大的,将由JIT编译成本地码运行,其它在JVM内解释执行,解释执行和编译执行的速度相差10倍以上。参见JVM的:globals.hpp product(bool, DontCompileHugeMethods, true,

\develop(intx, HugeMethodLimit, 8000,

\+DontCompileHugeMethods\

通过将文本不编译到字节码中,减少内存perm区占用,也能防止JIT失效。

3.2.5 减少反射调用,以及基本类型装箱。

因为模板输出的大量是基本类型和字符串,Httl遇到任何类似需要boxed和unboxed的地方,都会重载所有基本类型方法,以减少boxed和unboxed的处理。比如:当输出基本类型时,需要转成String,如果使用format(Object)接口,就会将基本类型装箱。

反射也经常是性能瓶颈所在,比如:因为int[]不继承于Object[],为了通用处理,改为Array.get(array, index)来获取数组的项,导致在Profiler分析时,发现80%的CPU时间都耗在Array.get()上。 等等。

4.设计

4.1 类关系

查看大图

4.1.1 核心领域模型划分原则:按实体域,服务域,会话域划分。

不管你做一个什么产品,都一定有一个被操作的主体,比如:服务框架管理的Service,任务框架管理的Task,Spring管理的Bean等,这就是实体域。 即然有被操作者,就一定有操作者,它管理被操作者的生命周期,发起动作,比如:服务框架的ServiceInvoker,,任务框架的TaskScheduler,Spring的BeanFactory等,这就是服务域。

服务域发起动作,在执行过程中,会有一些临时状态需要存储交换,比如:Invacation,Execution,Request等,这就是会话域。

相应的,在HTTL中:

Engine 为服务域,它是API的入口,并负责实体域Template的生命周期管理,它是Singleton实例的,加载后不可变,所以是线程安全的,它的初始化过程较重,请复用单例。

? Template 为实体域,代表着被操作者,它是Prototype实例的,即每个模板产生一个实例,加载后不可变,同样也是线程安全的,模板变化后,将产生不同的实例,而不改变原实例。 ? Context 为会话域,执有操作过程的可变状态,它是ThreadLocal实例的,即不和其它线程竞争使用,所以也是线程安全的,请不要跨线程传递,它的初始化轻量,每次执行新建实例,执行完即销毁。

?

这样划分的好处是,职责清晰,可变状态集中,每个域都是无锁线程安全的,保证在大并发下,不会降低系统的活性。

4.1.2 分包原则:按复用度,抽象度,稳定度分包。

复用度:每种用户所需用到的类,就是同一复用粒度的,比如:使用者和扩展者,这样可以减少代码干扰,以及最大化复用。 ? 抽象度:包中抽象类个数占比,比如包中有10个类,其中3个为抽象类(包括接口),则抽象度为3/10,保持被依赖者总是比依赖者的抽象度高,形成金子塔关系,这样可以保持每层都有足够的扩展性。

?

?

稳定度:被依赖包和依赖包的占比,如果一个包依赖很多包,那别的包变化都会引起它跟随变化,所以它就不稳定,反之即稳定,保持包的稳定度和抽象度成正比,即把抽象类(包括接口)放到稳定的包中,这样可以防止不稳定性传染。

稳定度与抽象度关系如下图:

也就是分包应该如下:

其中上面那个包不依赖其它包。所以它很稳定,应尽量把抽象类或接口放在这一层,

而下面那个包依赖了三个包,三个包变化会引起它跟随变化,所以它是不稳定,应尽量把具体实现类放在这一层。

因稳定度与抽象度成正比,所以不稳定度与抽象度成反比,计算方式如下: (1) I = Ce / (Ca + Ce)

I: Instability (不稳定度)

? Ca: Afferent Coupling (传入依赖,也就是被其它包依赖的个数) ? Ce: Efferent Coupling (输出依赖,也就是依赖其它包的个数)

?

(2) A = Na / Nc

A: Abstractness (抽象度)

? Na: Number of abstract classes (抽象类的个数) ? Nc: Number of classes (类的个数,包括抽象类)

?

(3) D = abs(1 - I - A) * sin(45) D: Distance (偏差)

? I: Instability (不稳定度) ? A: Abstractness (抽象度)

?

应该保持偏差越小越好,即下图所示交点都落在绿色反比线左右:

基于上面的原则,HTTL的包结构整体上划分为三层:

?

API (Application Programming Interface) 模板引擎的使用者依赖的接口类,也是核心领域模型,其中Engine类相当于微内核,只管理非功能性的扩展点的加载,不硬编码模板加载解析渲染的任何部分。

SPI (Service Provider Interface) 模板引擎的扩展者依赖的接口类,它依赖于API的领域模型,它是模板引擎功能正交分解的抽象层,以保证用户可以最小粒度替换需要改写的地方,方便二次开发。

? BUILT-IN (Built-in Implementation) 内置扩展实现,它是SPI标准实现,也是可被用户替换的类,它包含引擎所有做的事,包括扩展点之间的组装过程(可替换DefaultEngine),以确保没有功能换不掉,即平等对待扩展者。

?

采用子包依赖父包风格,所以将API放在根目录,SPI接口独立子包,各种实现放在SPI的下一级子包中。

使用者API导入: import httl.*; ? 扩展者SPI导入:import httl.spi.*;

?

下图是HTTL所有包的不稳定度与抽象度的比值距阵:

HTTL所有核心包都是靠近反比线的,即上图用绿色标识的,表示分包是合理的。

4.2 调用过程

查看大图

执行过程说明:(与上图中的序号对应) 1 当从引擎中获取模板时,

1.1 首先会在缓存查找是否已缓存,如果有缓存就直接返回, 1.2 如果没有,则加载模板, 1.3 接着进行模板语法解析,

1.3.1 在解析到表达式时,将其转译为Java表达式, 1.3.2 并对静态文本进行编译前过滤,比如删除空白等,

1.3.3 对解析后的Java代码进行编译,得到Template的具体模板实现类, 1.3.4 实例化模板实现类,

1.4 将模板实例写入缓存,并返回给用户, 2 当用户调用模板的渲染方法时,

2.1 将动态占位符内容,先格式化成字符串, 2.2 再进行过滤,比如转义动态内容的HTML特殊符, 2.3 然后输出过滤后的内容,如果是静态文本直接输出。

5.指令

基于Velocity指令和Html注释:

5.0 与Velocity对比

如果你用过Velocity模板,可以查看以下对比,加深了解:

5.0.1 语法对比

1. HTTL指令必需加注释外壳,只支持,不支持#if(x),确保不干扰HTML本身的有效源码。

2. HTTL指令中的变量不加$符,只支持,不支持,因为指令中没有加引号的字符串就是变量,和常规语言的语法一样,加$有点废话,而且容易忘写。 3. HTTL占位符必需加大括号,只支持${aaa},不支持$aaa,因为$在

JavaScript中也是合法变量名符号,而${}不是,减少混淆,也防止多人开发时,有人加大括号,有人不加,干脆没得选,都加,保持一致。 4. HTTL占位符当变量为null时输出空白串,而不像Velocity那样原样输出指令原文,即${aaa},等价于Velocity的$!{aaa},以免开发人员忘写感叹号,泄漏表达式源码,如需原样输出,可使用转义\\${aaa},在HTTL中,$!{aaa}表示不对内容进行过滤,用于原样输出HTML片段。

5. HTTL支持在所有使用变量的地方,进行表达式计算,也就是你不需要像Velocity那样,先#set($j = $i + 1),再#if($j == $m),可以直接,其它指令也一样。

6. HTTL采用扩展Class原生方法的方式,如:${\,而不像Velocity的Tool工具方法那样:$(StringTool.toChar(\,这样的调用方式更直观,更符合代码书写习惯。

5.0.2 指令对比

HTTL ${xxx.yyy} $!{xxx.yyy} \\# \\$ \\\\

Velocity $xxx.yyy ${xxx.yyy} $!xxx.yyy $!{xxx.yyy} ## ... #* ... *# #[[ ... ]]# \\# \\$ \\\\ 不支持

#set($xxx = $yyy) #if($xxx == $yyy)

功能 同

输出占位符 同

一个是不过滤,不

一个是不原样输同

出 相

不显示注释块 似

不解析文本块 似

特殊符转义 同

不定义输入参数类同 型 相

给变量赋值 同

条件判断 同

否则条件判断 同

否则判断 同

结束指令 同

列表循环 同

#elseif($xxx == $yyy)

#else #end

#foreach($item in $list)

#if($xxx == $yyy) #break 相

中断循环

#end 似 #macro($xxx)

相宏替换,模板片

似 断

相捕获块输出到变似 量中

相读取文本文件内似 容

相包含另一模板输似 出 相

表达式求值 似

不停止模板解析

#define($xxx) $!{read(\$!{include(\$!{evaluate(\不支持

#include(\#parse(\#evaluate(\#stop

5.1 输出指令

5.1.1 过滤输出

输出表达式的计算结果,并进行过滤,比如:过滤变量中的HTML标签,防止HTML注入攻击。 格式:

${expression}

示例:

${user.name}

5.1.2 不过滤输出

原样输出表达式的计算结果,不进行任何过滤,通常用于输出HTML片段。 格式:

$!{expression}

示例: $!{body}

5.2 变量定义指令 5.2.1 类型声明

声明输入变量的类型,模板内部其它变量类型基于此类型推导。 格式:

示例:

5.2.2 变量赋值

将表达式的计算结果存入变量中。

格式:

示例:

缺省模板中set的变量,是不会回写到Context的参数Map中的。你需要用“:=”进行赋值,才会回写到Context的参数Map中: 格式:

示例:

你可以通过ThreadLocal的Context.getContext().getParameters()拿到回写的变量:

// 你可以把入参设成不可修写的Map,不会影响运行。 Map parameters =

Collections.unmodifiableMap(parameters);

// 传入的parameters在渲染过程中总是不会被修复,确保渲染过程无副作用,以及多次渲染的幂等性。

template.render(parameters, writer);

// 注意:这里获取到的并不是上面render参入的parameters,而是parameters的包装类。

// 此parameters是可写的,当模板中set回写变量时,写入该Map中。 // 在查询变量时,先在包装类中查找,找不到,再到原生传入的parameters中查找。

Context.getContext().getParameters().get(\

5.3 条件指令

5.3.1 IF条件

如果条件表达式计算结果为真或非空,则输出指令所包含的块。 格式:

示例:

5.3.2 ELSEIF条件

如果前面的IF条件不为真,并且当前条件表达式计算结果为真或非空,则输出指令所包含的块。

格式:

示例:

5.3.3 ELSE条件

如果前面的IF条件不为真,则输出指令所包含的块。 格式:

示例:

...

5.4 迭代指令

5.4.1 集合迭代

迭代表达式产生的集合,以集合中的每项值,重复输出指令所包含的块。

格式:

示例:

...

${foreach.index} ${foreach.size} ${foreach.first} ${foreach.last}

5.4.3 中断迭代

当条件表达式为真或非空时,中断当前迭代过程。 格式:

示例:

...

...

5.5 宏指令

将指令块封装成可复用的模板片断,在声明处不执行,在调用时再执行。 格式:

示例:

... $!{xxx(books)}

5.6 字面指令

5.6.1 注释块

隐藏注释的内容,用于注解过程,或屏蔽指令内容。 格式:

示例:

5.6.2 不解析块

原样输出模板内容,用于输出纯文本内容。 格式:

示例:

5.6.3 特殊符转义

原样输出指令特殊符,用于输出纯文本内容。 格式:

\\#, \\$, \\\\

示例:

\\${xxx}

6.表达式

基于Java表达式和扩展方法。

支持Java所有表达式,以下只列出与Java不同的点: 所有null值的操作均返回null。

? 所有实现Comparable的对象都支持比较运算符。 ? 所有对象都支持逻辑与或,分别返回空值或非空值。

?

6.1 操作符表达式

6.1.1 集合操作符

Sequence: 1..3 List: [123, \

Map: [\

6.1.2 日期操作符

date1 > date2 date1 >= date2 date1 < date2 date1 <= date2

6.1.3 逻辑操作符

等价于:

等价于:

等价于:

等价于:

6.2 函数表达式

6.2.1 转型函数

obj.to(\num.toDate

str.toDate

str.toDate(\str.toChar str.toBoolean str.toByte str.toInt str.toLong str.toFloat str.toDouble str.toClass

6.2.2 转义函数

str.escapeXml str.escapeUrl str.escapeString

6.2.3 格式化函数

num.format(\num.format(\date.format(\

date.format(\

6.2.4 集合函数

array[index] list[index]

map.key map[\

${item.xxx}

${item.xxx}

${entry.key}

${entry.value}

sort(list)

cycle(item, item)

${colors.next}

6.2.5 文件函数

include(\

include(\include(locale(\

read(\

read(\read(locale(\

6.2.6 系统函数

now() random() uuid()

7.配置

配置中,+=表示在缺省配置上追加配置,=表示覆盖缺省配置。

7.1 模板引擎配置

engine=httl.spi.engines.DefaultEngine parser=httl.spi.parsers.CommentParser

translator=httl.spi.translators.DfaTranslator

其中,engine负责组装,parser负责解析语法,translator负责将模板表达式翻译成java表达式。除非你想改变语法,或优化解析性能,否则此三项不需要配置。

如果你喜欢HTML标签属性语法,可以配置: parser=httl.spi.parsers.AttributeParser 语法如:

${book.title}

如果属性和其它框架冲突,可以添加名字空间: attribute.namespace=httl

属性语法需要用到jericho包解析HTML标签: jericho-html-3.1.jar

net.htmlparser.jericho jericho-html 3.1 名称空间写法如:

你可以设置foreach状态的变量名,缺省为foreach:(缺省值不用配) foreach.status=foreach

7.2 模板缓存配置

缺省为强缓存,即所有模板和表达式加载后全部缓存:(缺省值不用配)

template.cache=java.util.concurrent.ConcurrentHashMap expression.cache=java.util.concurrent.ConcurrentHashMap

如果你的模板非常之多,内存不足以缓存所有模板,可以配置为LRU缓存: template.cache=httl.spi.caches.LruCache cache.capacity=10000

你可以用cache同时设置模板和表达式两者的缓存: cache=httl.spi.caches.LruCache 等价于:

template.cache=httl.spi.caches.LruCache expression.cache=httl.spi.caches.LruCache 如果不需要缓存,请设为: cache=null

7.4 模板加载配置

(1) 可以配置启动时预编译所有模板,会调用loader.list()扫描模板文件。 template.suffix用于loader.list()过滤模板文件。 预编译缺省关闭:(缺省值不用配) precompiled=false template.suffix=.httl

(2) 可以配置是否允许热加载,缺省为false:(缺省值不用配) reloadable=false

开启热加载后,模板引擎在getTemplate()时会检查文件的lastModified时间,如果比上次加载的时间新,就重新加载。

请注意:旧的模板不会被卸载,经常改文件会导致内存perm区越来越大,只能在开发阶段使用。

(3) 可以配置模板加载缺省编码,缺省为UTF-8:(缺省值不用配) input.encoding=UTF-8

7.4.1 从Classpath下加载

缺省从Classpath下加载,即模板放在任意jar包中:(缺省值不用配) loaders=httl.spi.loaders.ClasspathLoader template.directory=

7.4.2 从文件加载:

loaders=httl.spi.loaders.FileLoader

template.directory=/home/admin/templates

7.4.3 从jar包中加载:

loaders=httl.spi.loaders.JarLoader

template.directory=/home/admin/tempaltes.jar

7.4.4 从zip包中加载:

loaders=httl.spi.loaders.ZipLoader

template.directory=/home/admin/tempaltes.zip

7.4.5 从指定url加载:

loaders=httl.spi.loaders.UrlLoader

template.directory=http://myhost/tempaltes

7.4.6 从war包加载:

loaders=httl.spi.loaders.ServletLoader template.directory=/WEB-INF/templates 需在web.xml中配置ServletLoader的listener:

httl.spi.loaders.ServletLoader

7.4.7 从内存字符串加载:

loaders=httl.spi.loaders.StringLoader 然后编码加入模板内容:

StringLoader.addResource(%user)-->${user.name}\

7.4.8 从多个源加载:

loaders=httl.spi.loaders.ClasspathLoader,httl.spi.loaders.FileLoader 或者使用\保留缺省的classpath加载的同时,增加新的加载器: loaders+=httl.spi.loaders.FileLoader

7.5 模板编译器

用于将模板类编译成字节码,缺省使用jdk自带的编译器:(缺省值不用配) compiler=httl.spi.compilers.JdkCompiler 你也可以换成javassist编译:

compiler=httl.spi.compilers.JavassistCompiler 当然,也就需要增加javassist的jar包依赖: javassist-3.15.0-GA.jar

org.javassist javassist 3.15.0-GA (1) 输出选项:

如果设置output.stream=true,在编译期就会将模板文件转换成byte[]数据,在输出时,直接向OutputStream中输出byte[]流,以免运行期每次输出都要转一次。

缺省全部开启:(缺省值不用配) output.stream=true

output.writer=true

如果output.stream和output.writer同时开启,每份模板将编译成两份class,并返回自适应Template代理类。当用户调用template.render(Map,OutputStream)时,实际执行输出byte[]的Template类,当用户调用template.render(Map,Writer)时,实际执行输出String的Template类。

如果output.stream和output.writer同时关闭,只生成writer模板,相当于只开启output.writer。

注意:如果只开启output.stream=true,必须用

template.render(Map,OutputStream),否则数据转换会导致性能更低。如果只开启output.writer=true,必须用template.render(Map,Writer),否则数据转换会导致性能更低。

如果你从来不传入OutputStream或Writer,请关闭相应配置开关,减少编译开销。

(2) 内存选项:

缺省将模板源码和模板文本都不编译到字节码中:(缺省值不用配) source.in.class=false text.in.class=false

它通过一个Map缓存做中介,把模板源码和模板文本都放到了runtime属性中,以节省内存perm区大小。

编译到字节码中,在小模板时,可能会稍快,但不会有质的飞跃,在模板大于8K时,会导致JVM的JIT优化失效,会更慢,不建议改变此选项。 (3) 版本选项:

java版本的配置会影响字节码生成的版本。 java.version=1.6 (4) 调试选项:

如果你想知道编译后的字节码是什么样的,可以开启debug模式,并设置编译目录,编译器会向指定目录输出.class文件。 调试缺省关闭:(缺省值不用配) debug=false

compile.directory=

7.6 输出格式化器

缺省加载了日期格式化器:(缺省值不用配) formatters=httl.spi.formatters.DateFormatter date.format=yyyy-MM-dd HH:mm:ss time.zone=

你也可以设置时区,设置后,格式化的结果会带上时区的值:(缺省为当前系统时区) time.zone=+8

你可以使用\保留缺省的日期格式化器的同时,增加新的格式化器: formatters+=httl.spi.formatters.NumberFormatter number.format=###,##0.###

你还可以设置null,true,false值的输出,

null值缺省会输出空白,true,false原样输出:(缺省值不用配) null.value=

true.value=true false.value=false 比如可以配为: null.value=N/A true.value=yes false.value=no

7.7 输出过滤器

过滤器分为两类,一类是针对模板文本的,一类是针对动态插值的。

模板文本过滤会在编译的时候执行,编译时即把模板文本替换掉,不影响输出时的性能。

动态插值的过滤会在输出的时候执行,需小心过滤引起性能问题。

缺省加载了动态插值HTML过滤,防止HTML注入XSS攻击:(缺省值不用配)

value.filters=httl.spi.filters.EscapeXmlFilter text.filters=

你可以配置在编译时将静态文本中的空白行删除:(编译时执行,不影响渲染速度)

text.filters=httl.spi.filters.ClearBlankLineFilter

你也可以配置在编译时将静态文本中的连续空白符压缩成单个空格:(编译时执行,不影响渲染速度)

text.filters=httl.spi.filters.CompressBlankFilter 你也可以用filters同时设置value.filters和text.filters: filters=httl.spi.filters.CompressBlankFilter 等价于:

value.filters+=httl.spi.filters.CompressBlankFilter text.filters+=httl.spi.filters.CompressBlankFilter

7.8 类型导入

7.8.1 导入包名

import.packages=java.util

这样你就可以在模板内用短类名,而不用带上全包名。

7.8.2 导入方法

缺省导入DefaultMethod类中的方法:(缺省值不用配) import.methods=httl.spi.methods.DefaultMethod 你可以使用\保留缺省方法的同时,导入新的方法: import.methods+=com.foo.MyMethod

比如DefaultMethod有静态方法:(也可以是非静态方法) public static String format(Date self, String format) {

return ...; }

public static char toChar(String self) { return ...; }

public static Date now() { return ...; }

则可以在模板中调用: ${date.format(\${str.toChar()} ${now()}

原理:静态方法的第一个参数传入被操作者本身的引用,后面的参数调用时传入。 如果你需要setEngine()注入引擎实例,或setXxx(String)注入配置项,可以导入非静态方法,但静态方法编译后更快。 比如:

// 将注入引擎本身

private Engine engine;

public setEngine(Engine engine) { this.engine = engine; }

// 将注入httl.properties中input.encoding=UTF-8配置的值 private String inputEncoding;

public setInputEncoding(String inputEncoding) { this.inputEncoding = inputEncoding; }

// 非静态方法导入

public String include(String templateName) { return engine.getTemplate(templateName, inputEncoding).toString(); }

使用方式一样: ${include(\

7.8.3 导入宏

import.macros=

7.8.4 导入序列

用法如:

缺省包含星期和月份序列:(缺省值不用配)

sequences=Mon Tue Wed Thu Fri Sat Sun Mon,\\

Monday Tuesday Wednesday Thursday Friday Saturday Sunday Monday,\\ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Jan,\\

January February March May June July August September October November December January

你可以使用\保留缺省序列的同时,增加新的序列: sequences+=金 木 水 火 土 金

7.9 日志输出配置

缺省使用log4j输出日志:(缺省值不用配) loggers=httl.spi.loggers.Log4jLogger

如果你需要使用其它日志工具输出日志,可配置自己的适配器。 比如使用JDK的日志输出:

loggers=httl.spi.loggers.JdkLogger 你也可以同时输出到多个日志工具:

loggers=httl.spi.loggers.Log4jLogger,httl.spi.loggers.JdkLogger 或者使用\保留缺省的log4j输出的同时,增加新的输出: loggers+=httl.spi.loggers.JdkLogger

8.集成

HTTL在MVC中的定位:

1. Web应用需要配置ServletLoader加载模板:(所有Web集成通用) web.xml:

httl.spi.loaders.ServletLoader httl.properties:

loaders=httl.spi.loaders.ServletLoader

1. Web应用查找httl.properties配置的顺序:(所有Web集成通用) (1) 首先查找/WEB-INF/web.xml中的context-param,如:

httl.properties

/WEB-INF/httl.properties

(注:如果配置路径以 / 开头则表示在web应用目录下,否则在ClassPath下查找)

(2) 如果未配置,则查找默认WEB-INF路径:/WEB-INF/httl.properties (3) 如果WEB-INF中没有,则查找ClassPath根目录:httl.properties (4) 如果ClassPath根目录也没有,则使用标准配置。

8.1 Spring MVC 集成

配置/WEB-INF/web.xml:

xmlns:web=\ xsi:schemaLocation=\http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\ version=\

httl.spi.loaders.ServletLoader

org.springframework.web.context.ContextLoaderListener

springmvc

org.springframework.web.servlet.DispatcherServlet

1

springmvc /*

配置/WEB-INF/springmvc-servlet.xml:

\

class=\

配置/WEB-INF/httl.properties: loaders=httl.spi.loaders.ServletLoader template.directory=/WEB-INF/templates input.encoding=UTF-8 output.encoding=UTF-8

8.2 Struts 集成

配置/WEB-INF/web.xml:

xmlns:web=\ xsi:schemaLocation=\http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\ version=\

httl.spi.loaders.ServletLoader

struts

org.apache.struts2.dispatcher.FilterDispatcher

struts /*

配置classpath:struts.xml:

\ \

配置/WEB-INF/httl.properties: loaders=httl.spi.loaders.ServletLoader template.directory=/WEB-INF/templates input.encoding=UTF-8 output.encoding=UTF-8

8.3 Webx 集成

配置/WEB-INF/web.xml:

xmlns=\

xmlns:web=\ xsi:schemaLocation=\http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\ version=\

httl.spi.loaders.ServletLoader

webx

com.alibaba.citrus.webx.servlet.WebxFrameworkFilter

excludes *.css, *.js, *.jpg, *.gif, *.png

webx /*

配置/WEB-INF/webx.xml:

xmlns:services=\

xmlns:engines=\s\

xsi:schemaLocation=\http://www.springframework.org/schema/beans/spring-beans.xsd http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd

http://www.alibaba.com/schema/services/template/engines http://localhost:8080/schema/services/template/engines.xsd\

配置/WEB-INF/httl.properties: loaders=httl.spi.loaders.ServletLoader template.directory=/WEB-INF/templates input.encoding=UTF-8 output.encoding=UTF-8

8.4 Filter 集成

你需要在你的业务Servlet中,处理完业务后,将业务参数都写到

request.setAttribute()中。 HttlFilter会模板目录下读取请求path加.httl后缀的模板,然后以request中的变量进行渲染。 配置/WEB-INF/web.xml:

xmlns:web=\ xsi:schemaLocation=\http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\ version=\

httl.spi.loaders.ServletLoader

yourservlet

com.foo.YourServlet 1

yourservlet *.do

httl

httl.web.servlet.HttlFilter

httl *.do

配置/WEB-INF/httl.properties: loaders=httl.spi.loaders.ServletLoader template.directory=/WEB-INF/templates input.encoding=UTF-8 output.encoding=UTF-8

8.5 Servlet 集成

你需要在你的业务Servlet中,处理完业务后,将业务参数都写到request.setAttribute()中,并将请求forward到指定模板,比如:

request.getRequestDispatcher(\HttlServlet会模板目录下读取forward过来的path同名的模板,然后以request中的变量进行渲染。 配置/WEB-INF/web.xml:

xmlns:web=\ xsi:schemaLocation=\http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd\ version=\

httl.spi.loaders.ServletLoader

yourservlet

com.foo.YourServlet 1

yourservlet *.do

httl

httl.web.servlet.HttlServlet 2

httl *.httl

配置/WEB-INF/httl.properties: loaders=httl.spi.loaders.ServletLoader template.directory=/WEB-INF/templates input.encoding=UTF-8 output.encoding=UTF-8

9.扩展

9.1 配置注入:

public MyFilter implements Filter {

private String outputEncoding;

// 将注入httl.properties中的output.encoding=UTF-8配置项 public void setOutputEncoding(String outputEncoding) {

this.outputEncoding = outputEncoding; }

private Compiler compiler;

// 将注入httl.properties中的compiler配置项 // 并且将实例化和初始化好compiler的属性

public void setCompiler(Compiler compiler) { this.compiler = compiler; }

private Engine engine;

// 将注入Engine本身

public void setEngine(Engine engine) { this.engine = engine; }

public String filter(String value) { // ... } }

9.2 支持的扩展点

# 日志输出

loggers=httl.spi.Logger

# JAVA编译器

compiler=httl.spi.Compiler

# 模板语法解析器

parser=httl.spi.Parser

# 表达式翻译器

translator=httl.spi.Translator

# 模板加载器

loaders=httl.spi.Loader

# 模板缓存

template.cache=java.util.Map

# 表达式缓存

expression.cache=java.util.Map

# 插值过滤

value.filters=httl.spi.Filter

# 静态文本过滤

text.filters=httl.spi.Filter

# 插值格式化器

formatters=httl.spi.Formatter

# 方法扩展

import.methods=StaticMethodClass

10.下载

10.1 发布包:

https://github.com/httl/httl/downloads

10.2 源代码:

https://github.com/httl/httl 下载源码:

git clone https://github.com/httl/httl.git 编译源码:

mvn install -Dmaven.test.skip 性能测试:

mvn test -Dtest=httl.test.PerformanceTest -Dstream=false -Dsize=200 -Dcount=10000

生成Eclipse工程描述文件:

mvn eclipse:eclipse -DdownloadSources

导入Eclipse工程:

Eclipse -> File -> Import -> Existing Projects into Workspace -> Browse -> Finished

将.httl文件以html编辑器格式打开:

Eclipse -> Window -> Preferences -> General -> Content Types -> Text -> HTML -> Add -> .httl

10.3 Maven依赖:

httl

httl 1.0.2

httl-repository Httl Repository

http://httl.github.com/maven/

本文来源:https://www.bwwdw.com/article/x255.html

Top