前段時間接觸freemaker時,本來后端寫各接口運行正常,但加入了模板后,頻繁報空指針問題,整了許久,最后還是請教了別人解決了這個問題,現在記錄下來,方便以后碰到了可以查閱。
錯誤樣例如下:
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的時候,因為為空,報錯了,錯誤處的代碼是這樣的
<#if user>
其實准確的寫法應該是
<#if user??>
如果要消除錯誤,需要把前端代碼修后成后面這種形式,這讓一個后端開發的人來說很不合理啊,而且,模板中不止一處出現了這種寫法。
請教以后把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>
問題圓滿解決了。
從網上查找原因,發現下面這位哥們已經比較詳細的解釋了,就全文摘錄吧。
以下為轉載內容,點擊查看原文
在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”的由來)。(譯者注:以下的1、2、3、4、5、6是譯者自己加的,方便讀者看)
處理未定義的表達式,也就是說"expr"為null值。
1、作為像表達式“<assign varname=expr>”、“${expr}”、“ otherexpr == expr“、“otherexpr != expr”條件表達式或者是“hash[expr]”表達式的參數,這個參數將被當成空字符來對待。(譯者注:這里注意空字符和null是不一樣的).
2、作為“<list expr as item>”、“<foreach item in expr>”這樣的表達式的參數,其循環體將不會被執行,和list的長度為0是一樣的。
3、作為“<if>”或者其他布爾表達式命令的參數,空值將被當成是false來處理。非布爾數據模型或者邏輯操作數也可以放在“<if>”表達式中,空模型(長度為零的字符串,空的數組或者hash集合)都被當成是false來對待,其他情況下都被當成是true來處理。
4、當布爾值被當成字符串(比如用${...}輸出,或者是和其他字符串連接),true值將被轉換成“true”字符串處理,false值將被轉換成空字符串。
5、提供給<list>和<foreach>的標量數據模型參數將被當成只包含一個該模型的list來處理。(譯者注:就是說,傳給<list>和<foreach>的參數不是list或者數組類型的,而是單個元素,則會被當成只有一個元素的list或者數組)
6、“<include>”標簽的路徑參數將被作為絕對路徑處理。(譯者注:這里很多網上的文檔都沒有提過,是本人經過觀察發現的,然后從源碼和其注釋中找到的。在這種情況下,如果傳入的ftl路徑是相對路徑,則會報找不到文件的異常)。
在其他方面,甚至是在兼容模式下,這個Freemaker解析引擎是2.1引擎,你不會因此而丟掉其他新的功能。
以上就是譯文, 那么如果我們設置了全局的classic_compatible屬性,而在某個頁面上又不想遵守這個屬性該怎么辦呢?這樣就可以在當前這個頁面上采用以下的辦法,讓當前的頁面不再支持傳統模式:<#setting classic_compatible=false>