介绍:FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。 模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
记录Freemarker在项目中的配置与使用
准备
框架:Spring+SpringMvc+Mybatis
Freemarker:官网介绍
配置 Maven中需要引入的依赖 1 2 3 4 5 6 7 <freemarker.version > 2.3.20</freemarker.version > <dependency > <groupId > org.freemarker</groupId > <artifactId > freemarker</artifactId > <version > ${freemarker.version}</version > </dependency >
视图解析器,一般在spring-mvc.xml中配置 1 2 3 4 5 6 7 8 9 10 <bean id ="freemarkerResolver" class ="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver" > <property name ="viewClass" > <value > org.springframework.web.servlet.view.freemarker.FreeMarkerView</value > </property > <property name ="suffix" value =".ftl" /> <property name ="contentType" value ="text/html;charset=utf-8" /> <property name ="requestContextAttribute" value ="request" /> <property name ="order" value ="0" /> </bean >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <bean id ="freemarkerConfig" class ="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer" > <property name ="templateLoaderPaths" > <list > <value > /front-end/</value > <value > /front-end/main-frame</value > <value > /</value > </list > </property > <property name ="freemarkerSettings" > <props > <prop key ="template_update_delay" > 5</prop > <prop key ="default_encoding" > UTF-8</prop > <prop key ="locale" > UTF-8</prop > <prop key ="datetime_format" > yyyy-MM-dd HH:mm:ss</prop > <prop key ="time_format" > HH:mm:ss</prop > <prop key ="number_format" > 0.####</prop > <prop key ="boolean_format" > true,false</prop > <prop key ="whitespace_stripping" > true</prop > <prop key ="tag_syntax" > auto_detect</prop > <prop key ="url_escaping_charset" > UTF-8</prop > </props > </property > </bean >
前端.ftl文件结构
Controller.java 1 2 3 ModelAndView mav = new ModelAndView ();mav.setViewName("/main-frame" ); return mav;
公用布局模板拆分 使用freemarker的macro、import、include指令,我们可以将布局模板拆分为如下几个文件
/layout
defaultLayout.ftl
footer.ftl
header.ftl
sidebar.ftl
defaultLayout.ftl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <#macro layout> <html> <head> </head> <body> <div style="width: 700px; text-align:center; font-size:30px;"> <#include "header.ftl"> <#include "sidebar.ftl"> <#-- 在这里嵌入main content --> <#nested> <#include "footer.ftl"> </div> </body> </html> </#macro>
1 <div style ="background-color: #b4efb8;" > header</div >
1 2 3 <div style ="width:30%; height:300px; float:left; background-color: #8825ae;" > sidebar </div >
1 <div style ="background-color: #B3D3F3;" > footer</div >
那么在任何一个使用该布局的页面,我们只要写如下的代码,修改要嵌入到layout中的main content就好了。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <#-- 引入布局指令的命名空间 --> <#import "../layout/defaultLayout.ftl" as defaultLayout> <#-- 调用布局指令 --> <@defaultLayout.layout> <#-- 将下面这个main content嵌入到layout指令的nested块中 --> <div style="width:70%; height:300px; float:left; background-color: #12c5ae;"> main content</div> </@defaultLayout.layout> <#-- 引入布局指令的命名空间 --> <#import "../layout/defaultLayout.ftl" as defaultLayout> <#-- 调用布局指令 --> <@defaultLayout.layout> <#-- 将下面这个main content嵌入到layout指令的nested块中 --> <div style="width:70%; height:300px; float:left; background-color: #12c5ae;"> main content</div> </@defaultLayout.layout>
问题 使用模板拆分,遇到的问题: 1 2 3 4 5 6 Caused by: java.io.FileNotFoundException: Template "../main-frame/main-frame.ftl" not found. at freemarker.template.Configuration.getTemplate(Configuration.java:742) at freemarker.core.Environment.getTemplateForInclusion(Environment.java:1694) at freemarker.core.Environment.getTemplateForImporting(Environment.java:1748) at freemarker.core.LibraryLoad.accept(LibraryLoad.java:111) ... 48 more
处理方法:模板路径不对,需要找到模板所在文件夹的上一级再往下找。
freemarker默认配置使用时,如果传到前端的值为null或者不存在,后台会报错。 处理方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 <bean id ="freemarkerConfig" class ="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer" > <property name ="templateLoaderPath" value ="/template/" /> <property name ="freemarkerSettings" > <props > <prop key ="classic_compatible" > true</prop > <prop key ="defaultEncoding" > utf-8</prop > <prop key ="template_exception_handler" > rethrow</prop > </props > </property > </bean >
增加了一行:
1 <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、作为像表达式“”、“${expr}”、“ otherexpr == expr“、“otherexpr != expr”条件表达式或者是“hash[expr]”表达式的参数,这个参数将被当成空字符来对待。(译者注:这里注意空字符和null是不一样的). 2、作为“”、“”这样的表达式的参数,其循环体将不会被执行,和list的长度为0是一样的。 3、作为“”或者其他布尔表达式命令的参数,空值将被当成是false来处理。非布尔数据模型或者逻辑操作数也可以放在“”表达式中,空模型(长度为零的字符串,空的数组或者hash集合)都被当成是false来对待,其他情况下都被当成是true来处理。 4、当布尔值被当成字符串(比如用${…}输出,或者是和其他字符串连接),true值将被转换成“true”字符串处理,false值将被转换成空字符串。 5、提供给和的标量数据模型参数将被当成只包含一个该模型的list来处理。(译者注:就是说,传给和的参数不是list或者数组类型的,而是单个元素,则会被当成只有一个元素的list或者数组) 6、“”标签的路径参数将被作为绝对路径处理。(译者注:这里很多网上的文档都没有提过,是本人经过观察发现的,然后从源码和其注释中找到的。在这种情况下,如果传入的ftl路径是相对路径,则会报找不到文件的异常)。 在其他方面,甚至是在兼容模式下,这个Freemaker解析引擎是2.1引擎,你不会因此而丢掉其他新的功能。 以上就是译文, 那么如果我们设置了全局的classic_compatible属性,而在某个页面上又不想遵守这个属性该怎么办呢?这样就可以在当前这个页面上采用以下的办法,让当前的页面不再支持传统模式:<#setting classic_compatible=false>