这是一篇学习自慕课网视频--《Java模板引擎之Freemarker》的笔记,感谢慕课网和慕课老师们的无私帮助。写这篇博客的目的是为以后工作中使用freemarker,遇到问题方便查阅。
什么是Freemarker
- Freemarker是一款模板引擎
- Freemarker不是web框架,它只是一个Java组件,提供MVC设计模式中视图层的功能
数据模型+模板输出=HTML(输出)
创建Springboot集成Freemarker的项目
- 打开IntelliJ IDEA,依次点击File > new Project ,弹出窗口中选择Spring Initializr,出现如下窗口
2.依次点击Next,输入项目名称,团队名称,其他默认之后继续Next,出现如下窗口
3.如上窗口,依次勾选Core -> DevTools(Springboot热部署)、Web -> Web(Springmvc)、Template Engines -> Freemarker,选好后点击Next ,即可创建一个包含Freemarker的Springboot项目,此创建过程需要联网。
以上是pom文件包含的依赖。
4.项目创建好后,目录结构如下
5.打开application.yaml,加入如下配置项
# 配置freemarker
spring:
freemarker:
# 设置模板后缀名
suffix: .ftl
# 设置文档类型
content-type: text/html
# 设置页面编码格式
charset: UTF-8
# 设置页面缓存
cache: false
# 设置ftl文件路径
template-loader-path: classpath:/templates/
check-template-location: true
expose-request-attributes: true
expose-session-attributes: true
request-context-attribute: request
# 设置静态文件路径,js,css等
mvc:
static-path-pattern: /static/**
server:
port: 8080
servlet:
context-path: /springboot-java
6.创建HelloController,在controller中写入一个控制页面跳转的方法,如下
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class HelloController {
@RequestMapping("/")
public String welcome(HttpServletRequest request){
request.setAttribute("name","Mr.Yang");
return "index";
}
}
7.在templates文件夹下,新建index.ftl文件,写入如下内容
<#assign base=request.contextPath />
<!DOCTYPE html>
<html lang="zh">
<head>
<base id="base" href="${base}">
<title>首页</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
Hello! ${name},glad to see you!<br/>
${base}
</body>
</html>
8.启动DemoApplication,运行Springboot容器
看到如上内容,表示项目已经在tomcat上启动成功了,项目名称是demo,端口是8080,访问地址是http://localhost:8080/demo
9.访问http://localhost:8080/demo,即可看到Freemarker引擎生成的html页面
至此,Springboot集成Freemarker项目创建完毕。
Freemarker内置指令
- 变量创建和赋值和运算
<#assign a=10 /> <font color="red">${a + 100}</font>
-
自定义对象User变量的属性值获取
<ul> <li>对象</li> <font color="red">${(userObj.name)!"默认值"}</font><br/> </ul> <!-- (userObj.name)! 表示对userObj和name两个变量依次进行是否为空判断,如果有一个为空,则输出默认值 -->
-
输出富文本内容
user.setBrief("<font color='red'>我只想早点下班。对不起,你是程序员!</font>"); <ul> <li>输出富文本内容</li> ${(userObj.brief)!?html} </ul> <!-- 在(userObj.brief)!之后加上?html 表示调用freemarker的内置函数html 这样会输出html内容的源代码,不加就会输出html内容 -->
-
集合Map的遍历
<ul> <li>集合map的遍历</li> <#list map?keys as key> <font color="red">${key}:${map[key]}</font><br/> </#list> </ul>
-
if语法
<ul> <li>if else指令</li> <#if myList?exists> <!-- 也可写作 myList?? ,用来判断myList是否存在,不存在则不执行list循环 --> <#list myList as item> ${item} </#list> </#if> <li>if多条件 || , && , !</li> <#assign var='python'> <#if var == 'python' || var == 'java' || var?length==6> <font color="red">python or java</font><br/> </#if> </ul>
-
switch语法
<h2>switch case break default</h2> <ul> <#assgin var == 110 /> <#switch var> <#case 10> <#case 11> 10 or 11<br/> <#break> <#case 100> 100<br/> <#break> <#default> other </#switch> </ul>
-
string基本操作命令
<ul> <#assign a='hello' /> <#assign b='world'/> <li>连接</li> <font color="red">${a + b}</font><br/> <li>截取</li> <font color="red">${(a + b)?substring(5,8)}</font><br/> <li>长度</li> <font color="red">${(a + b)?length}</font><br/> <li>大写</li> <font color="red">${(a + b)?upper_case}</font><br/> <li>小写</li> <font color="red">${(a + b)?lower_case}</font><br/> <li>index_of</li> <font color="red">${(a + b)?index_of('w')}</font><br/> <li>last_index_of</li> <font color="red">${(a + b)?last_index_of('o')}</font><br/> <li>replace</li> <font color="red">${(a + b)?replace('o','xx')}</font><br/> </ul>
Freemarker自定义函数
- 编辑index.ftl文件,使用sort_int函数对集合list进行升序排序
<h1>自定义函数</h1> <h2>1,自定义函数(整数排序 sort_int)</h2> <div class="demo-div"> <ul> <#assign myList=[2,3,4,5,1,8,9,8,7]/> <li>未排序</li> <#list myList as item> ${item}, </#list> <li>已排序</li> <#list sort_int(myList) as item> ${item}, </#list> </ul> </div>
-
在model.tag包下,创建SortMethod类实现TemplateModelEX接口,它就是实现自定义排序函数的类。
package com.example.demo.model.tag; public class SortMethod implements TemplateMethodModelEx { @Override public Object exec(List arguments) throws TemplateModelException { //获取第一个参数 SimpleSequence arg0=(SimpleSequence)arguments.get(0); List<BigDecimal> list=arg0.toList(); Collections.sort(list,new Comparator<BigDecimal>(){ @Override public int compare(BigDecimal o1, BigDecimal o2) { return o1.intValue()-o2.intValue(); //升序 } }); return list; } }
-
在控制器方法中,加入sort_int函数变量。
@RequestMapping("/") public String welcome(HttpServletRequest request){ request.setAttribute("name","Mr.Yang"); request.setAttribute("sort_int",new SortMethod()); return "index"; }
-
运行springboot,查看localhost:8080/demo,结果如下则自定义函数创建成功。
list排序内置函数和常用指令
item_index:此变量用于list遍历指令内,表示当前item所在下标,从0开始计数。
sort :内置的list集合排序函数
reverse: 内置的list集合反序函数
size :获取list集合的大小
myList[3]:获取下标为3的集合元素
<#assign base=request.contextPath />
<!DOCTYPE html>
<html lang="zh">
<head>
<base id="base" href="${base}">
<title>首页</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>List的指令</h1>
<h2>1,list常用指令</h2>
<div class="demo-div">
<ul>
<#assign myList=[2,3,4,5,1,8,9,8,7] />
<#list myList?sort?reverse as item>
${item_index} : ${item}<br/>
</#list>
${myList?size}<br/>
${myList[3]}<br/>
</ul>
</div>
</body>
</html>
Freemarker自定义指令
- 创建index2.ftl,写入如下内容,使用自定义指令role,自定义以@开头
<#assign base=request.contextPath /> <!DOCTYPE html> <html lang="zh"> <head> <base id="base" href="${base}"> <title>index2</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <h1>自定义指令</h1> <h2>1,用户123456是否拥有admin角色,并且返回admin的权限</h2> <div class="demo-div"> <ul> <@role user='123456' role='admin'; result1 , result2> <#if result1> 我的角色是<font color="red">admin</font><br/> </#if> 我拥有的权限是:<font color="red"> <#list result2 as item> ${item}, </#list> </font><br/> </@role> </ul> </div> </body> </html>
-
在model.tag包下创建RoleDirectiveModel类,实现TemplateDirectiveModel接口,它就是创建role指令的实现类。
package com.example.demo.model.tag; @Component public class RoleDirectiveModel implements TemplateDirectiveModel { /** * * @param environment 环境变量 * @param params 指令参数(存储你所需要的值,随便是什么Key-Value你懂的) * @param loopVars 循环变量 返回值 * @param templateDirectiveBody 指令内容 * 除了params外,其他的都能是Null。 * @throws TemplateException * @throws IOException */ @Override public void execute(Environment environment, Map params, TemplateModel[] loopVars, TemplateDirectiveBody templateDirectiveBody) throws TemplateException, IOException { System.out.println("========="); TemplateScalarModel user=(TemplateScalarModel)params.get("user"); TemplateScalarModel role=(TemplateScalarModel)params.get("role"); if("123456".equals(user.getAsString()) && "admin".equals(role.getAsString()) ){ loopVars[0]=TemplateBooleanModel.TRUE; } List<String> otherRights=new ArrayList<>(); otherRights.add("add"); otherRights.add("update"); otherRights.add("delete"); loopVars[1]=new SimpleSequence(otherRights); templateDirectiveBody.render(environment.getOut()); } }
-
在config包下创建FreeMarkerAutoConfiguration类,这是一个java配置类,它用来将自定义指令role注册到freemarker的共享变量中,这样在模板文件中就可以使用role指令了。
package com.example.demo.config; import com.example.demo.model.tag.RoleDirectiveModel; import com.example.demo.model.tag.SortMethod; import freemarker.template.TemplateModelException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; @Slf4j @Configuration public class FreeMarkerAutoConfiguration { @Autowired private freemarker.template.Configuration configuration; @Autowired private RoleDirectiveModel roleDirectiveModel; @Autowired private SortMethod sortMethod; @PostConstruct public void setSharedVariable() { try { //自定义标签 configuration.setSharedVariable("role", roleDirectiveModel); configuration.setSharedVariable("sort_int",sortMethod); } catch (Exception e) { log.error("Custom tags failed to load:{}", e.getMessage()); } } }
-
重启Springboot项目,看到如下内容则表示自定义指令运行成功。
FreeMarker内建函数
- 处理字符串内建函数
//见名知义哈 1.substring , cap_first , ends_with , contains 2. date , datetime , time 3. starts_with , index_of , last_index_of , split , trim
代码示例
<h1>内建函数</h1> <div class="demo-div"> <ul> <h2>1,字符串内建函数</h2> <#list "a|b|c|d"?split("|") as item> <li>${item}</li> </#list> <!-- 字符串转日期 --> <#assign var1="01/03/2017"?date("MM/dd/yyyy")> <#assign var2="15:05:30"?time("HH:mm:ss")> <#assign var3="2016-12-31 03:05 PM"?datetime("yyyy-MM-dd hh:mm")> <li>${var1}</li> <li>${var2}</li> <li>${var3}</li> </ul> </div>
-
处理数字的内建函数
1. string , x?string("0.##") 2. round , floor , ceiling
代码示例
<h2>2,数字类型内建函数</h2> <!-- 数字类型内建函数 --> <#assign numVar1 = 314.5662 /> <li>${numVar1?string("0.##")}</li> //四舍五入 结果314.57 <li>${numVar1?round}</li>
-
处理List的内建函数
1. first , last, seq_contains, seq_index_of 2. size, reverse, sort, sort_by 3. chunk
代码示例
<h2>3,list内建函数</h2> <#assign listVar1 = [1,2,3,4,11,12,13,14,21,22,23,24] /> <li>${listVar1?chunk(4)?size}<?li> <#list listVar1?chunk(4)?last as item> <li>${item}</li> </#list>
-
其他内建函数
1. is函数:is_string, is_number, is_method 2. (), has_content函数 3. eval求值
代码示例
<h2>4,其他内建函数</h2> <#assign sVar='hello' /> <li>${sVar?is_number?string('yes','no')}</li> <li>${sVar?has_content?string('yes','no')}</li> <li>${'1+2'?eval}</li>
Macro宏指令、function函数指令
- 下面是macro指令和function指令的语法
#macro nested return 章节 macro语法: <#macro macro_name param1 param2 param3 paramN...> template_code ${param1} <#nested /> </#macro> 调用 <@macro_name param1="value1" param2="value2" /> <@macro_name param1="value1" param2="value2"> nested_template </@macro_name> #function语法 <#function function_name param1 param2> <#return param1+param2> </#function> 调用 ${doAdd(100,100)}
-
新建index3 .ftl文件,在其中使用macro宏指令创建共用模板代码,使用function指令创建自定义函数。
<#assign base=request.contextPath /> <!DOCTYPE html> <html lang="zh"> <head> <base id="base" href="${base}"> <title>index3</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <style type="text/css"> div { font-size: 20px; } .demo-div ul li { font-weight: bold; font-size: 20px; margin: 15px 0px 5px 0px; } </style> </head> <body style="padding-left: 50px;"> <h1>macro nested return 实战demo</h1> <h2>1,macro :宏指令</h2> <div class="demo-div"> <ul> <li>栗子1:无参数的macro</li> <div> <#macro test> <font color="red">我是无参数的macro</font> </#macro> <@test/> </div> <li>栗子2:有参数的macro</li> <div> <#macro test1 param1 param2> <font color="red">我是有参数的macro,param1 = ${param1},param2 = ${param2}</font> </#macro> <@test1 param1="java" param2="python" /> </div> <li>栗子3:有默认值参数的macro</li> <div> <#macro test2 param1 param2="python"> <font color="red">我是有参数的macro,param1 = ${param1},param2 = ${param2}</font> </#macro> <@test2 param1="java" /> </div> <li>栗子4:有多个参数的macro</li> <div> <#macro test3 param1 param2="python" paramExt...> <font color="red">我是有参数的macro,param1 = ${param1}, param2 = ${param2}</font> <br/> <font color="red">${paramExt['param3']}</font><br/> <font color="red">${paramExt['param4']}</font> </#macro> <@test3 param1="java" param2="hello python" param3="javascript" param4="nodejs"/> </div> </ul> </div> <h2>2,nested</h2> <div class="demo-div"> <ul> <#macro test param1 = "java"> ${param1}<br/> <#nested param1,"我的nested 参数"><br/> </#macro> <li>调用</li> <div> <@test param1="java"; loopVar1, loopVar2> <font color="red">hello ${loopVar1} , ${loopVar2}</font> </@test> <@test param1="python";loopVar1> hello ${loopVar1}<br/> </@test> </div> </ul> </div> <h2>3,函数</h2> <div class="demo-div"> <ul> <#function doAdd param1 param2> <#return param1+param2> </#function> <li>调用</li> <div>你好,我是调用 ${doAdd(100,100)}</div> </ul> </div> </body> </html>
-
运行springboot项目,在浏览器中访问 localhost:8080/index3,可看到如下结果: