Freemaker给我的工作提供了极大的便利,但经常后台代码运行正常,在加入了其他模板后(比如说分页模板),频繁报空指针(或者参数获取不到)问题,报错日志如下图:
1 八月 10, 2017 1:10:07 下午 org.apache.catalina.core.StandardWrapperValve invoke 2 严重: Servlet.service() for servlet [springMVC] in context with path [/jewelry_star] threw exception [Request processing failed; nested exception is freemarker.core._MiscTemplateException: When calling macro "pager", required parameter "code" (parameter #7) was not specified. 3 4 ---- 5 Tip: If the omission was deliberate, you may consider making the parameter optional in the macro by specifying a default value for it, like <#macro macroName paramName=defaultExpr>) 6 ---- 7 8 ---- 9 FTL stack trace ("~" means nesting-related): 10 - Failed at: @page.pager pageIndex=pageModel.pageI... [in template "goods/goodsList.html" at line 120, column 9] 11 ----] with root cause 12 FreeMarker template error: 13 When calling macro "pager", required parameter "code" (parameter #7) was not specified. 14 15 ---- 16 Tip: If the omission was deliberate, you may consider making the parameter optional in the macro by specifying a default value for it, like <#macro macroName paramName=defaultExpr>) 17 ---- 18 19 ---- 20 FTL stack trace ("~" means nesting-related): 21 - Failed at: @page.pager pageIndex=pageModel.pageI... [in template "goods/goodsList.html" at line 120, column 9] 22 ---- 23 24 Java stack trace (for programmers): 25 ---- 26 freemarker.core._MiscTemplateException: [... Exception message was already printed; see it above ...] 27 at freemarker.core.Macro$Context.sanityCheck(Macro.java:232) 28 at freemarker.core.Macro$Context.runMacro(Macro.java:181) 29 at freemarker.core.Environment.invoke(Environment.java:701) 30 at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84) 31 at freemarker.core.Environment.visit(Environment.java:324) 32 at freemarker.core.MixedContent.accept(MixedContent.java:54) 33 at freemarker.core.Environment.visitByHiddingParent(Environment.java:345) 34 at freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48) 35 at freemarker.core.Environment.visit(Environment.java:324) 36 at freemarker.core.MixedContent.accept(MixedContent.java:54) 37 at freemarker.core.Environment.visit(Environment.java:324) 38 at freemarker.core.Environment.process(Environment.java:302) 39 at freemarker.template.Template.process(Template.java:325) 40 at org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:367) 41 at org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:284) 42 at org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:234) 43 at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:167) 44 at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) 45 at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257) 46 at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037) 47 at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980) 48 at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) 49 at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 50 at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 51 at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) 52 at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 53 at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) 54 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) 55 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 56 at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 57 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 58 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 59 at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) 60 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 61 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 62 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 63 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) 64 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) 65 at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) 66 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) 67 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) 68 at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624) 69 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) 70 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) 71 at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) 72 at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) 73 at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) 74 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) 75 at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) 76 at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) 77 at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) 78 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 79 at java.lang.Thread.run(Unknown Source)
一般这个时候我就会去检查模板的宏导入处是否正确添加参数。但有时还需检查模板本身是否也有添加参数引用。
一般经过这两个步骤,问题就能顺利解决。
后来查阅网上资料,还有以下若干个解决方法:
(资料来源:博客园-Weagle http://www.cnblogs.com/Weagle/p/5417947.html)
一、书写正确规范代码
如代码应该是
1 <#if user??>
而不是
1 <#if user>
二、修改Freemarker的配置
因为如果要消除错误,需要把前端代码修后成后面这种形式,这对一个后端开发的人来说很不合理,而且,模板中不止一处出现了这种写法。
所以可以把Freemarker的配置修改如下:
1 <bean id="freemarkerConfig" 2 class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> 3 <property name="templateLoaderPath" value="/template/" /> 4 <property name="freemarkerSettings"> 5 <!-- 设置默认的编码方式,原先是GBK,需要设置成utf-8 --> 6 <props> 7 <!--用于解决前端报空指针问题--> 8 <prop key="classic_compatible">true</prop> 9 <prop key="defaultEncoding">utf-8</prop> 10 <prop key="template_exception_handler">rethrow</prop> 11 </props> 12 </property> 13 </bean>
原理:
以下为转载内容,点击查看原文
在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>