一、Spring Boot如何集成FreeMarker模板引擎?
添加依赖
1 2 3 4 | < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-freemarker</ artifactId > </ dependency > |
配置属性文件
.properties版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # 是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性。 spring.freemarker.allow-request-override=false # 是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性。 spring.freemarker.allow-session-override=false # 是否启用模板缓存。 spring.freemarker.cache=false # 模板编码。 spring.freemarker.charset=UTF-8 # 是否检查模板位置是否存在。 spring.freemarker.check-template-location=true # Content-Type value. spring.freemarker.content-type=text/html # 是否启用freemarker spring.freemarker.enabled=true # 设定所有request的属性在merge到模板的时候,是否要都添加到model中. spring.freemarker.expose-request-attributes=false # 是否在merge模板的时候,将HttpSession属性都添加到model中 spring.freemarker.expose-session-attributes=false # 设定是否以springMacroRequestContext的形式暴露RequestContext给Spring’s macro library使用 spring.freemarker.expose-spring-macro-helpers=true # 是否优先从文件系统加载template,以支持热加载,默认为true spring.freemarker.prefer-file-system-access=true # 设定模板的后缀. spring.freemarker.suffix=.ftl # 设定模板的加载路径,多个以逗号分隔,默认: spring.freemarker.template-loader-path=classpath:/templates # 设定FreeMarker keys. spring.freemarker.settings.template_update_delay=0 spring.freemarker.settings.default_encoding=UTF-8 spring.freemarker.settings.classic_compatible=true |
.yml版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | spring: # FreeMarker模板引擎 freemarker: # 是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性。 allow-request-override: false # 是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性。 allow-session-override: false # 是否启用模板缓存。 cache: false # 模板编码。 charset: UTF-8 # 是否检查模板位置是否存在。 check-template-location: true # Content-Type value. content- type : text /html # 是否启用freemarker enabled: true # 设定所有request的属性在merge到模板的时候,是否要都添加到model中. expose-request-attributes: false # 是否在merge模板的时候,将HttpSession属性都添加到model中 expose-session-attributes: false # 设定是否以springMacroRequestContext的形式暴露RequestContext给Spring’s macro library使用 expose-spring-macro-helpers: true # 是否优先从文件系统加载template,以支持热加载,默认为true prefer- file -system-access: true # 设定模板的后缀. suffix: .ftl # 设定模板的加载路径,多个以逗号分隔,默认: template-loader-path: classpath: /templates # 设定FreeMarker keys. settings: template_update_delay: 0 default_encoding: UTF-8 classic_compatible: true |
编写Controller
1 2 3 4 5 6 7 8 | @Controller public class FreemarkController { @RequestMapping ( "/" ) public String index(Model model) { return "/index" ; } } |
页面
1 2 3 4 5 6 7 8 9 10 11 | <!DOCTYPE html> < html lang = "en" > < head > < title >SpringBoot + Freemarker</ title > < meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" /> </ head > < body > < h1 >Hello boy,</ h1 >< br > < p >当前时间:${.now?string("yyyy-MM-dd HH:mm:ss.sss")}</ p > </ body > </ html > |
二、常用的FreeMarker语法
下面详细介绍在ftl模板中如何使用列表、map、字符串、数字、日期、switch以及macro宏指令等语法。
修改下controller,传递一些需要处理的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @RequestMapping ( "/" ) public String index(Model model) { Map map = new LinkedHashMap<>(); for ( int i = 0 ; i < 5 ; i++) { map.put( "key" + i, "value" + i); } model.addAttribute( "list" , Arrays.asList( "string1" , "string2" , "string3" , "string4" , "string5" , "string6" )); model.addAttribute( "map" , map); model.addAttribute( "name" , " htTps://wWw.zHyD.mE " ); model.addAttribute( "htmlText" , "<span style=\"color: red;font-size: 16px;\">html内容</span>" ); model.addAttribute( "num" , 123.012 ); model.addAttribute( "null" , null ); model.addAttribute( "dateObj" , new Date()); model.addAttribute( "bol" , true ); return "/index" ; } |
重写index.ftl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | <!DOCTYPE html> < html lang = "en" > < head > < title >Freemarker 语法大全</ title > < meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" /> < style > html { font-size: 14px; font-weight: 400; } .exp { font-size: 12px; color: lightgray; } </ style > </ head > < body > < p >当前时间:${.now?string("yyyy-MM-dd HH:mm:ss.sss")}</ p > < dl > < dt >list长度:< span class = "exp" >${list?size}</ span ></ dt > < dt >列表</ dt > <#list list as item> < dd >${item }, 索引:${item_index },hasNext:${item_has_next}</ dd > </#list> < dt >数字遍历</ dt > <#list 1..3 as item> < dd >数字${item}</ dd > </#list> < dt >map</ dt > <#if map?exists> <#list map?keys as key> < dd >${map[key]}, 索引:${key_index },hasNext:${key_has_next}</ dd > </#list> </#if> </ dl > < dl > < dt >字符串</ dt > < dd >普通字符串:< span class = "exp" >${name}</ span ></ dd > < dd >非html编码:< span class = "exp" >${htmlText}</ span ></ dd > < dd >html编码:< span class = "exp" >${htmlText?html}</ span ></ dd > < dd >首字母大写:< span class = "exp" >${name?cap_first}</ span ></ dd > < dd >首字母小写:< span class = "exp" >${name?uncap_first}</ span ></ dd > < dd >全小写:< span class = "exp" >${name?lower_case}</ span ></ dd > < dd >全大写:< span class = "exp" >${name?upper_case}</ span ></ dd > < dd >去除首位空格:< span class = "exp" >${name?trim}</ span ></ dd > < dd >空字符串:< span class = "exp" >${null?if_exists}</ span ></ dd > < dd >是否包含某个字符串:< span class = "exp" >${name?contains("wWw")?string}</ span ></ dd > < dd >默认值:< span class = "exp" >${null?default("空值默认")}</ span ></ dd > < dd >“${name}”字符串长度:< span class = "exp" >${name?length}</ span ></ dd > < dd >定义字符串:< span class = "exp" >str=码一码<#assign str="码一码"/></ span ></ dd > < dd >字符串拼接(1):< span class = "exp" >${"字符串拼接 + " + str}</ span ></ dd > < dd >字符串拼接(2):< span class = "exp" >${"字符串拼接 + ${str}"}</ span ></ dd > < dd >字符串截取单个字符(1):< span class = "exp" >${str[1]}</ span ></ dd > < dd >字符串截取(2):< span class = "exp" >${str?substring(1)}</ span ></ dd > < dd >字符串截取(3):< span class = "exp" >${str?substring(1,2)}</ span ></ dd > < dd >indexOf:< span class = "exp" >${str?index_of("一")}</ span ></ dd > < dd >split分割字符串:< span class = "exp" > <#list "a|b|c"?split("|") as item> ${item} </#list> </ span ></ dd > < dd >if...elseif...else:< span class = "exp" > <#if null == ''> 匹配if显示 <#elseif null == '1'> 匹配elseif显示 <#else> 匹配else显示 </#if></ span > </ dd > </ dl > < dl > < dt >switch</ dt > < dd > <#switch str> <#case "你好"> 匹配“你好” <#break > <#case "码一码"> 匹配“码一码” <#break > <#default> 默认匹配 </#switch> </ dd > </ dl > < dl > < dt >数字</ dt > < dd >普通数字:< span class = "exp" >${num}</ span ></ dd > < dd >数字类型:< span class = "exp" >${num?string.number}</ span ></ dd > < dd >货币类型:< span class = "exp" >${num?string.currency}</ span ></ dd > < dd >百分比类型:< span class = "exp" >${num?string.percent}</ span ></ dd > < dd >格式化数字:< span class = "exp" >${num?string("#.###")}</ span ></ dd > < dd >取数字的整数部分:< span class = "exp" >${num?int}</ span ></ dd > </ dl > < dl > < dt >运算符</ dt > < dd >不等于:!= < span class = "exp" >例如:${(1 != 2)?string('1 != 2', '1 == 2')}</ span ></ dd > < dd >等于:== < span class = "exp" >例如:${(1 == 1)?string('1 == 1', '1 != 1')}</ span ></ dd > < dd >大于(1):> < span class = "exp" >例如:${(2 > 1)?string('2 > 1', '2 < 1 ')}。<strong>注:使用> 时必须加括号,否则可能会被当成普通的标签闭合符号而引起报错</ strong ></ span > </ dd > < dd >大于(2):gt < span class = "exp" >例如:${(2 gt 1)?string('2 gt 1', '2 lte 1')}</ span ></ dd > < dd >大于等于:gte < span class = "exp" >例如:${(2 gte 2)?string('2 gte 2', '2 lt 2')}</ span ></ dd > < dd >小于(1):< < span class = "exp" >例如:${(1 < 2 )?string('1 < 2', '1 > 2')}。< strong >注:使用< 时必须加括号,否则可能会被当成普通的标签闭合符号而引起报错</ strong ></ span > </ dd > < dd >小于(2):lt < span class = "exp" >例如:${(1 lt 2)?string('1 lt 2', '1 gte 2')}</ span ></ dd > < dd >小于等于:lte < span class = "exp" >例如:${(2 lte 2)?string('2 lte 2', '2 gt 2')}</ span ></ dd > </ dl > < dl > < dt >boolean</ dt > < dd >普通boolean输出:< span class = "exp" >${bol}</ span ></ dd > < dd >boolean判断输出:< span class = "exp" >${bol?string('true的时候显示','false的时候显示')}</ span ></ dd > </ dl > < dl > < dt >日期</ dt > < dd >${dateObj?date}</ dd > < dd >${dateObj?time}</ dd > < dd >${dateObj?string("yyyy-MM-dd HH:mm:ss.SSS")}</ dd > </ dl > < dl > < dt >import</ dt > < dd > <#import "import.ftl" as importObj> < p >${importObj.importStr}</ p > < p >${importObj.importStr1}</ p > </ dd > </ dl > < dl > < dt >macro宏模板</ dt > < dd > <#macro listMacro title items> < p >${title?cap_first}: < ul > <#list items as item> < li >${item?cap_first}</ li > </#list> </ ul > <#nested > </#macro> </ dd > < dd > <@listMacro items=["item1", "item2", "item3"] title="Items"> nested标签表示可以插入自定义的内容 </@listMacro> </ dd > </ dl > include <#include "eclipse.ftl"> </ body > </ html > |
三、通过FreeMarker生成静态html
首先需要编写一个可以在普通类中获取到SpringBean的工具类SpringContextHelper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package com.wanma.helper; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * Spring上下文 组手类 */ @Component public class SpringContextHelper implements ApplicationContextAware { private static ApplicationContext appContext = null ; /** * 通过name获取 Bean. */ public static Object getBean(String name) { return appContext.getBean(name); } /** * 通过class获取Bean. */ public static <T> T getBean(Class<T> clazz) { return appContext.getBean(clazz); } /** * 通过name,以及Clazz返回指定的Bean */ public static <T> T getBean(String name, Class<T> clazz) { return appContext.getBean(name, clazz); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (appContext == null ) { appContext = applicationContext; } } } |
然后编写一个生成静态html文件的工具类FreemarkerHelper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package com.wanma.helper; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; import java.io.IOException; import java.util.Map; /** * Freemarker 助手类 */ public class FreemarkerHelper { public static Configuration cfg = SpringContextHelper.getBean(Configuration. class ); /** * 解析模板 */ public static String parseTpl(String tplName, Map<String, Object> params) { String html = "" ; Template tpl; try { tpl = cfg.getTemplate(tplName + ".ftl" ); html = FreeMarkerTemplateUtils.processTemplateIntoString(tpl, params); } catch (IOException | TemplateException e) { e.printStackTrace(); } return html; } } |
为了方便查看, 添加一个mapping
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @RequestMapping ( "/createHtml" ) @ResponseBody public String createHtml(Model model){ Map map = new LinkedHashMap<>(); for ( int i = 0 ; i < 5 ; i++) { map.put( "key" + i, "value" + i); } model.addAttribute( "list" , Arrays.asList( "string1" , "string2" , "string3" , "string4" , "string5" , "string6" )); model.addAttribute( "map" , map); model.addAttribute( "name" , " htTps://wWw.zHyD.mE " ); model.addAttribute( "htmlText" , "<span style=\"color: red;font-size: 16px;\">html内容</span>" ); model.addAttribute( "num" , 123.012 ); model.addAttribute( "null" , null ); model.addAttribute( "dateObj" , new Date()); model.addAttribute( "bol" , true ); return FreemarkerHelper.parseTpl( "index" , model.asMap()); } |
ok,访问/createHtml
到此为止,本篇已详细介绍了freemarker的使用方法。
源码: