前段时间接触freemaker时,原本后端写各接口运行正常,但加入了模板后,频繁报空指针问题,整了许久,最后仍是请教了别人解决了这个问题,如今记录下来,方便之后碰到了能够查阅。前端
错误样例以下:java
ERROR: freemarker.runtime - Error executing FreeMarker template FreeMarker template error: The following has evaluated to null or missing: ==> user [in template "include/header.ftl" at line 4, column 14] ---- Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #if user [in template "include/header.ftl" at line 4, column 9] - Reached through: #include "/include/header.ftl" [in template "index.ftl" at line 6, column 1] ---- Java stack trace (for programmers): ---- freemarker.core.InvalidReferenceException: [... Exception message was already printed; see it above ...] at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:131) at freemarker.core.UnexpectedTypeException.newDesciptionBuilder(UnexpectedTypeException.java:77)
能够看到,在读取user的时候,由于为空,报错了,错误处的代码是这样的web
<#if user>
其实准确的写法应该是spring
<#if user??>
若是要消除错误,须要把前端代码修后成后面这种形式,这让一个后端开发的人来讲很不合理啊,并且,模板中不止一处出现了这种写法。express
请教之后把freemarker的配置修改以下:后端
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/template/" /> <property name="freemarkerSettings"> <!-- 设置默认的编码方式,原先是GBK,须要设置成utf-8 --> <props> <!--用于解决前端报空指针问题--> <prop key="classic_compatible">true</prop> <prop key="defaultEncoding">utf-8</prop> <prop key="template_exception_handler">rethrow</prop> </props> </property> </bean>
添加了一行数组
<prop key="classic_compatible">true</prop>
问题圆满解决了。ui
从网上查找缘由,发现下面这位哥们已经比较详细的解释了,就全文摘录吧。编码
如下为转载内容,点击查看原文lua
在freemarker中的空值的处理,默认状况以${xxx}的方式取值会报错,咱们通常都采用${xxx?if_exists} 的方式去处理,烦死人了。通过查资料,不少人都建议使用classic_compatible=true的方式来处理,目测单词的意思应该是:“兼容传统模式”的意思。可是通过使用发现这个属性设置为true时,也有不少其余问题,好比boolean值的处理,好比include指令必须使用绝对路径,总之也会带来不少烦人的事情。最后找到源码,在Freemarker源码的Configurable类的isClassicCompatible方法上找到了详细的注释,这里翻译下,不过本人英语比较差,可能会有错误,若是有人不肯定能够去看源码。
原注释大意以下:
该方法返回Freemarker模板解析引擎是否工做在“Classic Compatibile”模式下。若是这个模式被激活,则Freemarker模板解析引擎将以如下的方式工做:(相似于1.7.x这个版本的运行方式,这个也是1.7.x的版本被称为“经典的Freemarker”的由来)。(译者注:如下的一、二、三、四、五、6是译者本身加的,方便读者看)
处理未定义的表达式,也就是说"expr"为null值。
一、做为像表达式“<assign varname=expr>”、“${expr}”、“ otherexpr == expr“、“otherexpr != expr”条件表达式或者是“hash[expr]”表达式的参数,这个参数将被当成空字符来对待。(译者注:这里注意空字符和null是不同的).
二、做为“<list expr as item>”、“<foreach item in expr>”这样的表达式的参数,其循环体将不会被执行,和list的长度为0是同样的。
三、做为“<if>”或者其余布尔表达式命令的参数,空值将被当成是false来处理。非布尔数据模型或者逻辑操做数也能够放在“<if>”表达式中,空模型(长度为零的字符串,空的数组或者hash集合)都被当成是false来对待,其余状况下都被当成是true来处理。
四、当布尔值被当成字符串(好比用${...}输出,或者是和其余字符串链接),true值将被转换成“true”字符串处理,false值将被转换成空字符串。
五、提供给<list>和<foreach>的标量数据模型参数将被当成只包含一个该模型的list来处理。(译者注:就是说,传给<list>和<foreach>的参数不是list或者数组类型的,而是单个元素,则会被当成只有一个元素的list或者数组)
六、“<include>”标签的路径参数将被做为绝对路径处理。(译者注:这里不少网上的文档都没有提过,是本人通过观察发现的,而后从源码和其注释中找到的。在这种状况下,若是传入的ftl路径是相对路径,则会报找不到文件的异常)。
在其余方面,甚至是在兼容模式下,这个Freemaker解析引擎是2.1引擎,你不会所以而丢掉其余新的功能。
以上就是译文, 那么若是咱们设置了全局的classic_compatible属性,而在某个页面上又不想遵照这个属性该怎么办呢?这样就能够在当前这个页面上采用如下的办法,让当前的页面再也不支持传统模式:<#setting classic_compatible=false>