Configuration.getTemplate(
...
)
方法,
FreeMarker就要把模板文件加载到内存中然后来解析它
(除非模板已经在
Configuration
对象中被
缓存
了)。
在这期间,有两种异常可能发生:
因模板文件没有找到而发生的
IOException
异常,
或在读取文件时发生其他的I/O问题。比如没有读取文件的权限,或者是磁盘错误。
这些错误的发出者是
TemplateLoader
对象
,可以将它作为插件设置到
Configuration
对象中。(为了正确起见:这里所说的”文件”,
是简化形式。例如,模板也可以存储在关系型数据库的表中。这是
TemplateLoader
所要做的事。)
根据FTL语言的规则,模板文件发生语法错误时会导致
freemarker.core.ParseException
异常。当获得
Template
对象
(
Configuration.getTemplate(
...
)
)时,
这种错误就会发生,而不是当执行
(
Template.process(
...
)
)模板的时候。
这种异常是
IOException
的一个子类。
freemarker.template.TemplatException
异常。
比如,一个频繁发生的错误,就是当模板引用一个不存在的变量。
默认情况下,当
TemplatException
异常发生时,
FreeMarker会用普通文本格式在输出中打印出FTL的错误信息和堆栈跟踪信息,
然后再次抛出
TemplatException
异常而中止模板的执行,
就可以捕捉到
Template.process(
...
)
方法抛出的异常了。而这种行为是可以定制的。FreeMarker也会经常写
TemplatException
异常的
TemplateException
异常在模板处理期间的抛出是由
freemarker.template.TemplateExceptionHandler
对象控制的,这个对象可以使用
setTemplateExceptionHandler(
...
)
方法配置到
Configuration
对象中。
TemplateExceptionHandler
对象只包含一个方法:
void handleTemplateException(TemplateException te, Environment env, Writer out)
throws TemplateException;
无论
TemplateException
异常什么时候发生,这个方法都会被调用。
异常处理是传递的
te
参数控制的,
模板处理的运行时(Runtime,译者注)环境可以访问
env
变量,
处理器可以使用
out
变量来打印输出信息。
如果方法抛出异常(通常是重复抛出
te
),那么模板的执行就会中止,
而且
Template.process(
...
)
方法也会抛出同样的异常。如果
handleTemplateException
对象不抛出异常,那么模板将会继续执行,就好像什么也没有发生过一样,
但是引发异常的语句将会被跳过(后面会详细说)。
当然,控制器仍然可以在输出中打印错误提示信息。
任何一种情况下,当
TemplateExceptionHandler
被调用前,
FreeMarker 将会记录异常
日志
。
我们用实例来看一下,当错误控制器不抛出异常时,
FreeMarker是如何跳过出错''语句''的。假设我们已经使用了如下模板异常控制器:
class MyTemplateExceptionHandler implements TemplateExceptionHandler {
public void handleTemplateException(TemplateException te, Environment env, java.io.Writer out)
throws TemplateException {
try {
out.write("[ERROR: " + te.getMessage() + "]");
} catch (IOException e) {
throw new TemplateException("Failed to print error message. Cause: " + e, env);
cfg.setTemplateExceptionHandler(new MyTemplateExceptionHandler());
如果错误发生在非FTL标记(没有被包含在
<#
...
>
或
<@
...
>
之间)的插值中,
那么整个插值将会被跳过。那么下面这个模板
(假设
badVar
在数据模型中不存在):
如果我们使用了
MyTemplateExceptionHandler
,就会打印:
a[ERROR: Expression badVar is undefined on line 1, column 4 in test.ftl.]b
而下面这个模板也会打印相同信息(除了报错的列数位置会不同...):
因为像这样来写时,只要插值内发生任何错误,整个插值都会被跳过。
如果错误发生在指令调用中参数的计算时,或者是指令参数列表发生问题时,
或在
<@
exp
...
>
中计算
exp
时发生错误,或者
exp
不是用户自定义的指令,
那么整个指令调用都会被跳过。例如:
将会输出:
a[ERROR: Expression badVar is undefined on line 1, column 7 in test.ftl.]b
请注意,错误发生在
if
指令的开始标签
(
<#if badVar>
)中,但是整个指令的调用都被跳过了。
从逻辑上说,嵌套的内容(
Foo
)也被跳过了,
因为嵌套的内容是受被包含的指令(
if
)控制(打印)的。
下面这个的输出也是相同的(除了报错的列数会不同...):
a<#if "foo${badVar}" == "foobar">Foo</#if>b
因为,正如这样来写,在参数处理时发生任何一个错误,
整个指令的调用都将会被跳过。
如果错误发生在已经开始执行的指令之后,那么指令调用将不会被跳过。
也就是说,如果在嵌套的内容中发生任何错误:
<#if true>
${badVar}
或者在一个宏定义体内:
<@test />
<#macro test>
${badVar}
</#macro>
那么输出将会是:
[ERROR: Expression badVar is undefined on line 4, column 5 in test.ftl.]
FreeMarker 本身带有这些预先编写的错误控制器:
TemplateExceptionHandler.DEBUG_HANDLER
:
打印堆栈跟踪信息(包括FTL错误信息和FTL堆栈跟踪信息)和重新抛出的异常。
这是默认的异常控制器(也就是说,在所有新的
Configuration
对象中,它是初始化好的)。
TemplateExceptionHandler.HTML_DEBUG_HANDLER
:
和
DEBUG_HANDLER
相同,但是它可以格式化堆栈跟踪信息,
那么就可以在Web浏览器中来阅读错误信息。
当你在制作HTML页面时,建议使用它而不是
DEBUG_HANDLER
。
TemplateExceptionHandler.IGNORE_HANDLER
:
简单地压制所有异常(但是要记住,FreeMarker 仍然会写日志)。
它对处理异常没有任何作用,也不会重新抛出异常。
TemplateExceptionHandler.RETHROW_HANDLER
:
简单重新抛出所有异常而不会做其它的事情。
这个控制器对Web应用程序(假设你在发生异常之后不想继续执行模板)来说非常好,
因为它在生成的页面发生错误的情况下,给了你很多对Web应用程序的控制权
(因为FreeMarker不向输出中打印任何关于该错误的信息)。
要获得更多在Web应用程序中处理错误的信息,可以
参见FAQ
。