• springboot整合freemarker


    前言

    本篇文章主要介绍的是springboot整合freemarker填充ftl模板文件,生成新的文件(如html),以及freemarker的语法。

    GitHub源码链接位于文章底部。

    freemarker介绍

    freemarker是一款模板引擎,它基于模板来生成文本输出。这里的文本包括但不限于html页面,word,各种源代码文本......

    工作原理


    模板:就是一份已经写好了基本内容,有着固定格式的文档,其中空出或者用占
    位符标识的内容,由使用者来填充,不同的使用者给出的数据是不同的。在模板
    中的占位符,在模板运行时,由模板引擎来解析模板,并采用动态数据替换占位
    符部分的内容。

    freemarker的应用方向有两个,一是基于ftl文件,将内容填充到ftl文件中,就可以使用制作ftl模板的文本的方式进行访问和显示了,比如使用html文本制作了一个ftl模板,我们使用代码填充数据进ftl模板,那么我们就能以访问html的方式去打开这个文件了;另一种方式则是直接生成对应的文件,比如生成xxx.html的文件。

    应用场景
    淘宝中的商品数不胜数,在商品的详情页这一块,如果全都以真实的html页面显示,那么有多少个商品就得有多少个页面了,何况还有增删改的情况。所以使用一个固定的ftl模板,填充数据,这样一个文件就能显示无数个页面的内容了。

    再比如一些政府单位的项目,每天需要发送一些word文档给领导,此时只需要通过程序将数据填充进ftl模板,然后生成一个个的xxx.word文件就行了。

    ftl指令

    1.一些常见的符号说明:

    ${}插值; 只能输出数值、日期或者字符串,其它类型不能输出。
    在ftl页面中添加如下代码:

    <#--这是 freemarker 注释,不会输出到文件中-->
    <h1>${name}; ${message}</h1>
    

    在freemarker接口里添加数据如下:

    这一段代码在页面输出的就是:

    <#freemarker 命令
    <#-- 注释 -->
    <@使用自定义命令
    ??是判断对象是否存在
    ?函数调用

    2.assign

    此指令用于在页面上定义一个变量。 语法: <#assign name=value>

    <#--assign-->
    <#--简单类型-->
    <#assign linkman="李四"/>
    联系人:${linkman}
    <#assign info={"tel":"110","sex":"男"}/>
    电话:${info.tel};性别:${info.sex}
    

    页面输出如下:

    3. include

    语法: <#include path>
    说明: path 参数可以是如 "foo.ftl"和 "../foo.ftl"一样的相对路径,或者是如"/foo.ftl"这样的绝对路径。

    创建模板文件 header.ftl

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>嵌入页面</title>
    </head>
    <body>
    <h1>嵌入页面</h1>
    </body>
    </html>
    

    在index模板文件中使用 include 指令引入 header.ftl 模板文件

    <#--include-->
    <#include "header.ftl"/>
    

    访问接口,在index.ftl视图如下:

    4.if

    <#if condition>
    ...
    <#elseif condition2>
    ...
    <#elseif condition3>
    ...
    ...
    <#else>
    ...
    </#if>
    这里:
    condition , condition2 等:表达式将被计算成布尔值。

    关键字: gt :比较运算符“大于”; gte :比较运算符“大于或等于”; lt :比较
    运算符“小于”; lte :比较运算符“小于或等于”

    <#--if-->
    <#assign bool=false/>
    <#if bool>
        bool为true
    <#else>
        bool为false
    </#if>
    

    这里定义一个bool为false,然后使用if进行判断,最后输出为false,
    页面显示为:bool为false

    5.list

    语法:
    <#list sequence as item>
    ...
    </#list>
    这里:
    sequence :表达式将被算作序列或集合
    item :循环变量(不是表达式)的名称
    如果想在循环中得到索引,使用循环变量+_index 就可以得到。如上述语法中则可以使用 item_index 可以得到循环变量

    在freemarker接口里添加数据如下:

    在index.ftl中遍历

    <#--list-->
    <#list goodsList as fruit>
        索引:${fruit_index},
        水果:${fruit.name};
        价格:${fruit.price}
    </#list>
    

    页面显示为:

    6.内建函数
    6.1使用 size 函数来实现对于集合大小的获取:
    <#--获取集合总记录数-->
    总共${goodsList?size}条记录
    <#--集合中索引为1的name属性-->
    ${goodsList[1].name}
    <#--集合中第一个元素的name属性-->
    ${goodsList?first.name}
    

    显示如下:“总共2条记录 香蕉 苹果 ”

    6.2可以使用 eval 将 json 字符串转换为对象
    <#assign str="{'id':'123','text':'文本'}"/>
    <#assign jsonObj=str?eval/>
    id为:${jsonObj.id};text为:${jsonObj.text}
    

    显示如下:“id为:123;text为:文本”

    6.3日期格式化

    在freemarker接口里添加数据如下:

    dataModel.put("today", new Date());
    

    ftl页面添加:

    当前日期:${today?date}<br>
    <hr>
    当前时间:${today?time}<br>
    <hr>
    当前日期+时间:${today?datetime}
    <hr>
    格式化显示当前日期时间:${today?string('yyyy年MM月dd日 HH:mm:ss')}
    

    显示如下:

    6.4数字转换为字符串

    在freemarker接口里添加数据如下:

    dataModel.put("number", 123456789L);
    

    index.ftl模板中添加:

    <#--数值显示处理-->
    ${number}
    

    显示如下:“123,456,789”
    发现数字会以每三位一个分隔符显示,有些时候不需要这个分隔符,就需要将数
    字转换为字符串,使用内建函数 c

    ${number?c}
    

    显示如下:“123456789”

    7.空值处理

    在 FreeMarker 中对于空值必须手动处理。
    ${emp.name!} 表示 name 为空时什么都不显示
    ${emp.name!(“名字为空”)} 表示 name 为空时显示 名字为空
    ${(emp.company.name)!} 表示如果 company 对象为空则什么都不显示, !只用
    在最近的那个属性判断;所以如果遇上有自定义类型(导航)属性时,需要使用括号
    ${bool???string} 表示:首先??表示判断 bool 变量是否存在,存在返回 true 否则 false,然后对返回的值调用其内置函数 string
    <#if str??> 表示去判断 str 变量是否存在,存在则 true,不存在为 false

    ${strs!"strs空值的默认显示值"}
    <hr>
    <#if strs??>
        str变量存在
    <#else >
        strs变量不存在
    </#if>
    

    显示如下:

    8.运算符
    8.1算数运算符

    FreeMarker 表达式中完全支持算术运算,FreeMarker 支持的算术运算符包括:+, - ,
    *, / , %

    8.2逻辑运算符

    逻辑运算符有如下几个:
    逻辑与:&&
    逻辑或:||
    逻辑非:!
    逻辑运算符只能作用于布尔值,否则将产生错误

    8.3. 比较运算符

    表达式中支持的比较运算符有如下几个:
    1 =或者==:判断两个值是否相等.
    2 !=:判断两个值是否不等.
    3 >或者 gt:判断左边值是否大于右边值
    4 >=或者 gte:判断左边值是否大于等于右边值
    5 <或者 lt:判断左边值是否小于右边值
    6 <=或者 lte:判断左边值是否小于等于右边值

    =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相
    同类型的值,否则会产生错误,而且 FreeMarker 是精确比较,"x","x ","X"是不等的.其
    它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用 gt
    等字母运算符代替>会有更好的效果,因为 FreeMarker 会把>解释成 FTL 标签的结
    束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>

    springboot整合freemarker

    首先来看一下项目结构:

    创建一个springboot项目

    pom文件中添加依赖
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-parent</artifactId>
            <version>2.1.3.RELEASE</version>
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
        </dependencies>
    
    application.yml中添加freemarker的相关配置
    server:
      port: 8080
    spring:
      freemarker:
        #设置编码格式
        charset: utf-8
        #设置文件后缀
        suffix: .ftl
        #设置ftl文件路径
        template-loader-path: classpath:/templates
        #关闭缓存,及时刷新,上线生产环境需要改为true
        cache: false
    

    在resources目录中添加templates文件夹因为这在yml中配置了。

    template文件夹中添加两个ftl模板文件,待会儿会用到,因为是创建html的文件,所以直接新建一个html文件,再将后缀改为ftl就行了。

    index.ftl代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Freemarker 测试</title>
    </head>
    <body>
    <#--这是 freemarker 注释,不会输出到文件中-->
    <h1>${name}; ${message}</h1>
    <hr>
    <#--assign-->
    <#--简单类型-->
    <#assign linkman="李四"/>
    联系人:${linkman}
    <#assign info={"tel":"110","sex":"男"}/>
    电话:${info.tel};性别:${info.sex}
    <hr>
    <#--include-->
    <#include "header.ftl"/>
    <hr>
    <#--if-->
    <#assign bool=false/>
    <#if bool>
        bool为true
    <#else>
        bool为false
    </#if>
    <hr>
    <#--list-->
    <#list goodsList as fruit>
        索引:${fruit_index},
        水果:${fruit.name};
        价格:${fruit.price}<br>
    </#list>
    <hr>
    <#--获取集合总记录数-->
    总共${goodsList?size}条记录
    <#--集合中索引为1的name属性-->
    ${goodsList[1].name}
    <#--集合中第一个元素的name属性-->
    ${goodsList?first.name}
    <hr>
    <#assign str="{'id':'123','text':'文本'}"/>
    <#assign jsonObj=str?eval/>
    id为:${jsonObj.id};text为:${jsonObj.text}<p></p>
    <hr>
    当前日期:${today?date}<br>
    <hr>
    当前时间:${today?time}<br>
    <hr>
    当前日期+时间:${today?datetime}
    <hr>
    格式化显示当前日期时间:${today?string('yyyy年MM月dd日 HH:mm:ss')}
    <hr>
    <#--数值显示处理-->
    ${number}
    <hr>
    ${number?c}<p></p>
    <hr>
    ${strs!"strs空值的默认显示值"}
    <hr>
    <#if strs??>
        str变量存在
    <#else >
        strs变量不存在
    </#if>
    
    </body>
    </html>
    

    header.ftl代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>嵌入页面</title>
    </head>
    <body>
    <h1>嵌入页面</h1>
    </body>
    </html>
    

    在freemarker接口中添加代码,返回freemarker的index.ftl视图

    @RequestMapping("/freemarkerIndex")
    public String freemarker(Map<String, Object> dataModel) {
    dataModel.put("name", "张三");
    dataModel.put("message", "hello world");
    
    List<Map<String,Object>> list = new ArrayList<>();
    Map<String, Object> map1 = new HashMap<>();
    map1.put("name", "苹果");
    map1.put("price", 4.5);
    Map<String, Object> map2 = new HashMap<>();
    map2.put("name", "香蕉");
    map2.put("price", 6.3);
    list.add(map1);
    list.add(map2);
    dataModel.put("goodsList", list);
    dataModel.put("today", new Date());
    dataModel.put("number", 123456789L);
    return "index";
    }
    

    ps:这里dataModel的map只能从方法参数中获取,如果是自己new的去存储数据,填充到模板中,会报空值异常。
    启动程序,通过localhost:8080/freemarkerIndex 访问该接口,得到页面如下:

    上面这个接口是直接返回视图,
    接下来是通过同样的数据,生成index.html静态文件。
    因为这里是不用返回的,如果做成接口的话,虽然能成功生成文件,但是会报404,所以这里用的是单元测试,实际开发中,如果是返回的json格式,而不是视图,就可以做成接口。

    @Test
    public void createFileByFreemarker() throws IOException, TemplateException {
    	//创建配置对象
    	Configuration configuration = new Configuration(Configuration.getVersion());
    	//设置默认生成文件编码
    	configuration.setDefaultEncoding("utf-8");
    	//设置模板路径
    	configuration.setClassForTemplateLoading(this.getClass(), "/templates");
    	//获取模板
    	Template template = configuration.getTemplate("index.ftl");
    	//加载数据
    	Map<String, Object> dataModel  =new HashMap<>();
    	dataModel.put("name", "张三");
    	dataModel.put("message", "hello world");
    
    	List<Map<String,Object>> list = new ArrayList<>();
    	Map<String, Object> map1 = new HashMap<>();
    	map1.put("name", "苹果");
    	map1.put("price", 4.5);
    	Map<String, Object> map2 = new HashMap<>();
    	map2.put("name", "香蕉");
    	map2.put("price", 6.3);
    	list.add(map1);
    	list.add(map2);
    
    	dataModel.put("goodsList", list);
    	dataModel.put("today", new Date());
    	dataModel.put("number", 123456789L);
    	//创建输出对象,将文件输出到D盘根目录下
    	FileWriter fileWriter = new FileWriter("D:/index.html");
    	//渲染模板和数据
    	template.process(dataModel, fileWriter);
    	//关闭输出
    	fileWriter.close();
    }
    

    本文GitHub源码:https://github.com/lixianguo5097/springboot/tree/master/springboot-freemarker

    CSDN:https://blog.csdn.net/qq_27682773
    简书:https://www.jianshu.com/u/e99381e6886e
    博客园:https://www.cnblogs.com/lixianguo
    个人博客:https://www.lxgblog.com

  • 相关阅读:
    既然选择了远方,就只顾风雨兼程!
    slots
    面向对象
    模块和作用域
    偏函数
    python中decorator
    返回函数
    filter, sort
    map/reduce
    开发步骤
  • 原文地址:https://www.cnblogs.com/lixianguo/p/12519012.html
Copyright © 2020-2023  润新知