一、引言
1、什么是Freemarker
FreeMarker是一款模板引擎,基于模板和要改变的数据,并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。它帮助从开发人员(Java 程序员)中分离出网页设计师(HTML设计师)。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
2、Freemarker模板组成部分
FreeMarker模板文件主要由如下4个部分组成:
-
文本:直接输出的部分
-
注释:使用<#-- ... -->格式做注释,里面内容不会输出
-
插值:即${...}或#{...}格式的部分,类似于占位符,将使用数据模型中的部分替代输出
-
FTL指令:即FreeMarker指令,全称是:FreeMarker Template Language,和HTML标记类似,但名字前加#予以区分,不会输出
3、FreeMarker与Web容器无关
FreeMarker与Web容器无关,即在Web运行时,它并不知道Servlet或HTTP,故此FreeMarker不仅可以用作表现层的实现技术,而且还可以用于生成XML,JSP或Java等各种文本文件。
在Java Web领域,FreeMarker是应用广泛的模板引擎,主要用于MVC中的view层,生成html展示数据给客户端,可以完全替代JSP。
FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写,模板中没有业务逻辑,外部Java程序通过数据库操作等生成数据传入模板(template)中,然后输出页面。它能够生成各种文本:HTML、XML、RTF、Java源代码等等,而且不需要Servlet环境,并且可以从任何源载入模板,如本地文件、数据库等等。
4、优缺点
①优点
FreeMarker的诞生是为了取代JSP。虽然JSP功能强大,可以写Java代码实现复杂的逻辑处理,但是页面会有大量业务逻辑,不利于维护和阅读,更不利于前后台分工,容易破坏MVC结构,所以舍弃JSP,选择使用FreeMarker是大势所趋。当前很多企业使用FreeMarker取代JSP,FreeMarker有众多的优点,如下所示:
-
很好地分离表现层和业务逻辑
JSP功能很强大,它可以在前台编写业务逻辑代码,但这也带来了一个很大的弊端——页面内容杂乱,可读性差,这将会大大增加后期的维护难度。而FreeMarker职责明确,功能专注,仅仅负责页面的展示,从而去掉了繁琐的逻辑代码。FreeMarker的原理就是:模板+数据模型=输出,模板只负责数据在页面中的表现,不涉及任何的逻辑代码,而所有的逻辑都是由数据模型来处理的。用户最终看到的输出是模板和数据模型合并后创建的。
-
提高开发效率
众所周知,JSP在第一次执行的时候需要转换成Servlet类,之后的每次修改都要编译和转换。这样就造成了每次修改都需要等待编译的时间,效率低下。而FreeMarker模板技术并不存在编译和转换的问题,所以就不会存在上述问题。相比而言,使用FreeMarker可以提高一定的开发效率。
-
明确分工
JSP页面前后端的代码写到了一起,耦合度很高,前端开发需要熟悉后台环境,需要去调试,而后台开发人员需要去做不熟悉的前端界面设计。对两者而言,交替性的工作需要花费一定的学习成本,效率低下。而使用FreeMarker后,前后端完全分离,大家各干各的,互不影响。
-
简单易用,功能强大
FreeMarker支持JSP标签,宏定义比JSP Tag方便,同时内置了大量常用功能,比如html过滤,日期金额格式化等等。FreeMarker代码十分简洁,上手快,使用非常方便。
综合以上得到以下:
- 彻底的分离表现层和业务逻辑,使得页面内容更加清晰,后期修改和维护更加方便。
- 提高开发效率,因为FreeMarker模板技术不存在编译和转换的问题。
- 使得开发过程中的人员分工更加明确,程序员和界面设计人员可以更好地协作。
③缺点
- 无法在模板中使用Java代码,这可能会限制一些复杂逻辑的实现。
- 相对于JSP来说,FreeMarker的模板语法较为简单,可能无法满足一些复杂的页面需求。
二、Freemarker常见指令
1、处理不存在的值
-
不存在值检测操作符
使用形式: unsafe_expr??
或 (unsafe_expr)??
这个操作符告诉我们一个值是否存在。基于这种情况, 结果是
true
或false
。
<#if name??>
存在
<#else>
不存在
</#if>
-
exists用在逻辑判断
exists用作逻辑判断,返回的是true或者false。
<#if name?exists>
${name}
</#if>
-
if_exists用来打印东西
if_exists用于输出的时候,如果存在则输出,不存在就输出空字符串。
${name?if_exists}
2、assign
使用该指令你可以创建一个新的变量, 或者替换一个已经存在的变量。语法格式如下:
<#assign name1=value1 name2=value2 ... nameN=valueN>
或
<#assign name>
hello
</#assign>
案例演示:
<#-- 创建一个str的变量 -->
<#assign str="hello">
<#-- 输出str -->
${str} <br>
<#-- 一次创建多个变量 -->
<#assign num=1 names=["zs","ls","ww"] >
${num} -- ${names?join(",")}
3、if/elseif/else
你可以使用 if
, elseif
和 else
指令来条件判断是否越过模板的一个部分。 *condition*
必须计算成布尔值, 否则错误将会中止模板处理。elseif
和 else
必须出现在 if
内部 (也就是,在 if
的开始标签和结束标签之间)。 if
中可以包含任意数量的 elseif
(包括0个) 而且结束时 else
是可选的。
<#if 条件1>
...
<#elseif 条件2>
...
<#elseif 条件3>
...
...
<#else>
...
</#if>
4、list
list
指令执行在 list
开始标签和 list
结束标签 ( list
中间的部分) 之间的代码, 对于在序列(或集合)中每个值指定为它的第一个参数。 对于每次迭代,循环变量将会存储当前项的值。
循环变量仅仅存在于 list
标签体内。 而且从循环中调用的宏/函数不会看到它(就像它只是局部变量一样)。
<#list s as item>
${s.name}
<#else>
暂无
</#list>
在
list
中的else
仅从 FreeMarker 2.3.23 版本开始支持。
注意:
-
else
部分是可选的, 而且仅仅从 FreeMarker 2.3.23 版本开始支持。 -
sequence
: 将我们想要迭代的项,算作是序列或集合的表达式 -
item
: 循环变量的名称 (不是表达式)
<#list arrs as item>
${item}
<#else>
集合是空的
</#list>
5、include
可以使用它在你的模板中插入另外一个 FreeMarker 模板文件 (由 path
参数指定)
<#include path>
或
<#include path options>
这里:
-
path
: 要包含文件的路径; -
options
: 一个或多个这样的选项:encoding=encoding
,parse=parse
-
encoding
: 算作是字符串的表达式 -
parse
: 算作是布尔值的表达式(为了向下兼容,也接受一部分字符串值) -
ignore_missing
: 算作是布尔值的表达式
-
<h1>Hello Freemarker</h1>
<#include "/common/head.ftl">
三、SpringBoot整合Freemarker
1、配置
引入依赖pom.xml。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
添加application.yml或application.properties文件配置
spring:
# freemarker
freemarker:
# 设置模板后缀名
suffix: .ftl
# 设置文档类型
content-type: text/html
# 设置页面编码格式
charset: UTF-8
# 设置页面缓存
cache: false
# 设置ftl文件路径
template-loader-path: classpath:/templates
# 设置静态文件路径,js,css等
mvc:
static-path-pattern: /static/**
2、新建模板
这里Freemarker选项需要到settings(设置) -> Editor(编辑器) -> File And Code Templates(文件和代码模板)中进行配置。扩展最好还是用ftl。
最后,请选择resources/templates目录,右键 New -> Freemarker 新建模板文件(.ftl)。
创建FreemarkerController,并配置跳转页面路径:
@Controller public class FreemarkerController { @RequestMapping("/") public String index() { return "index"; } }
四、综合案例
新建一个common.ftl文件引入我们的资源.比如我们的jQuery的资源
<#assign ctx="${springMacroRequestContext.contextPath}"> <link rel="stylesheet" href="${ctx}/bootstrap-3.4.1-dist/css/bootstrap.css"> <script src="${ctx}/jquery-3.6.1.js"></script> <script src="${ctx}/bootstrap-3.4.1-dist/js/bootstrap.js"></script>
在index.ftl里面进行页面的编写。
<!doctype html> <html lang="zh"> <head> <title>index</title> <style> td { padding: 20px; text-align: center; } </style> <#include "common.ftl"> </head> <body> <a href="${ctx}/save">增加</a> <#if Books??> <table class="table table-striped"> <#list Books as book> <tr> <td>${book.id}</td> <td>${book.bookname}</td> <td>${book.price}</td> <td>${book.booktype}</td> <td> <a href="${ctx}/del?id=${book.id}">删除</a> <a href="${ctx}/saveEdit?id=${book.id}">修改</a> </td> </tr> </#list> </table> </#if> </body> </html>
在这之前我们调用好我们的方法并且能实现,最好我们调用Controller层。
import com.example.springboot01.entity.TBook; import com.example.springboot01.service.TBookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import java.util.List; @Controller public class FreemarkerController { @Autowired private TBookService tBookService; @RequestMapping("/") public String index(Model model) { List<TBook> tBooks = tBookService.selectAll(); model.addAttribute("Books", tBooks); return "index"; }
运行我们的spring boot访问看结果。
save.ftl文件代码
<#--初始化--> <#--${book = null}--> <#--${edit = false}--> <#--检查是否选择/存在了--> <#--<#if book??>--> <#-- ${edit = true}--> <#-- ${book = books[bookId]}--> <#--</#if>--> <html> <head> <title>Books</title> </head> <body> <#--<h1>Books</h1>--> <#if book??> <h1>修改</h1> <form method="post" action="/update"> <input type="hidden" name="id" value="${book.id}"> <div class="form-group"> <label for="bookname">书名:</label> <input type="text" class="form-control" id="bookname" name="bookname" value="${book.bookname}" placeholder="请输入书籍名称"> </div> <div class="form-group"> <label for="price">价格:</label> <input type="number" class="form-control" id="price" name="price" value="${book.price}" placeholder="请输入书籍价格"> </div> <div class="form-group"> <label for="booktype">书籍类型:</label> <select class="form-control" id="booktype" name="booktype"> <option>历史</option> <option>言情</option> <option>悬疑</option> <option>恐怖</option> <option>科幻</option> <option>诡异</option> <option>玄幻</option> <option>爱情</option> <option>冒险</option> <option>热血</option> </select> </div> <button type="submit" class="btn btn-primary">提交</button> </form> <#else> <h1>增加</h1> <form method="post" action="/add"> <div class="form-group"> <label for="bookname">书名:</label> <input type="text" class="form-control" id="bookname" name="bookname" value="" placeholder="请输入书籍名称"> </div> <div class="form-group"> <label for="price">价格:</label> <input type="number" class="form-control" id="price" name="price" value="" placeholder="请输入书籍价格"> </div> <div class="form-group"> <label for="booktype">书籍类型:</label> <select class="form-control" id="booktype" name="booktype"> <option>历史</option> <option>言情</option> <option>悬疑</option> <option>恐怖</option> <option>科幻</option> <option>诡异</option> <option>玄幻</option> <option>爱情</option> <option>冒险</option> <option>热血</option> </select> </div> <button type="submit" class="btn btn-primary">提交</button> </form> </#if> </body> </html>
分享就到这里感谢观看,欢迎大家在评论区讨论!