FreeMarker基础知识

1、总览

思维导图
官网:http://freemarker.foofun.cn/
视频地址:https://www.bilibili.com/video/BV1zZ4y1u7iA

2、FreeMarker概述

2.1 FreeMarker概念

FreeMarker 是⼀款 模板引擎: 即⼀种基于模板和要改变的数据, 并⽤来⽣成输出⽂本(HTML⽹⻚,电⼦邮件,配置⽂件,源代码等)的通⽤⼯具。 是⼀个Java类库。
FreeMarker 被设计⽤来⽣成 HTML Web ⻚⾯,特别是基于 MVC 模式的应⽤程序,将视图从业务逻辑中抽离处理,业务中不再包括视图的展示,⽽是将视图交给 FreeMarker 来输出。虽然 FreeMarker 具有⼀些编程的能⼒,但通常由 Java 程序准备要显示的数据,由 FreeMarker ⽣成⻚⾯,通过模板显示准备的数据(如下图)
架构图

FreeMarker不是⼀个Web应⽤框架,⽽适合作为Web应⽤框架⼀个组件。
FreeMarker与容器⽆关,因为它并不知道HTTP或Servlet。FreeMarker同样可以应⽤于⾮Web应⽤程序环境。
FreeMarker更适合作为Model2框架(如Struts)的视图组件,你也可以在模板中使⽤JSP标记库。

2.2 FreeMarker特性

[1]通用目标

能够⽣成各种⽂本:HTML、XML、RTF、Java 源代码等等
易于嵌⼊到你的产品中:轻量级;不需要 Servlet 环境
插件式模板载⼊器:可以从任何源载⼊模板,如本地⽂件、数据库等等
可以按你所需⽣成⽂本:保存到本地⽂件;作为 Email 发送;从 Web 应⽤程序发送它返回给 Web 浏览器

[2]强大的模板语言

所有常⽤的指令:include、if/elseif/else、循环结构
在模板中创建和改变变量
⼏乎在任何地⽅都可以使⽤复杂表达式来指定值
命名的宏,可以具有位置参数和嵌套内容
名字空间有助于建⽴和维护可重⽤的宏库,或将⼤⼯程分成模块,⽽不必担⼼名字冲突
输出转换块:在嵌套模板⽚段⽣成输出时,转换HTML转义、压缩、语法⾼亮等等;你可以定义⾃⼰的转换

[3]通用数据模型

FreeMarker不是直接反射到Java对象,Java对象通过插件式对象封装,以变量⽅式在模板中显示
可以使⽤抽象(接⼝)⽅式表示对象(JavaBean、XML⽂档、SQL查询结果集等等),告诉模板开发者使⽤⽅法,使其不受技术细节的打扰

[4]为Web准备

在模板语⾔中内建处理典型Web相关任务(如HTML转义)的结构
能够集成到Model2 Web应⽤框架中作为JSP的替代
⽀持JSP标记库
为MVC模式设计:分离可视化设计和应⽤程序逻辑;分离⻚⾯设计员和程序员

3、基本语法

3.1 注释语法

<!--
    html注释语法
    浏览器中注释可见
-->
<#--
    freemarker 注释语法
        浏览器中注释不可见
        1.在FreeMarker中,html所有标签均使用
        2.js/css均适用,语法一致
-->
 
  ${msg}

3.2 数据类型

Freemarker 模板中的数据类型由如下⼏种:

布尔型:等价于 Java 的 Boolean 类型,不同的是不能直接输出,可转换为字符串输出
⽇期型:等价于 java 的 Date 类型,不同的是不能直接输出,需要转换成字符串再输出
数值型:等价于 java 中的 int,float,double 等数值类型,有三种显示形式:数值型(默认)、货币型、百分⽐型
字符型:等价于 java 中的字符串,有很多内置函数
sequence 类型:等价于 java 中的数组,list,set 等集合类型
hash 类型:等价于 java 中的 Map 类型

布尔类型

Java代码

/**
 * 布尔类型
 *
 * @author  Promsing(张有博)
 * @since  2022/12/31 - 19:27
 * @version 1.0.0
 */
@WebServlet("/f02")
public class Freemarker02 extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    

        req.setAttribute("flag",true);
        //转发给ftl文件中
        req.getRequestDispatcher("template/fmk02.ftl").forward(req,resp);
    }
}



HTML文件

<#--
    布尔类型
    布尔类型不能在FreeMarker页面输出,需要改成String输出
        方式一: ?c,推荐
        方式一: ?string('ture','false')
    官网:http://freemarker.foofun.cn/ref_builtins_boolean.html

-->
<h2>布尔类型</h2>
<h2>${flag?c}</h2>
<h2>${flag?string}</h2>
<h2>${flag?string('喜欢','不喜欢')}</h2>



日期类型

Java文件

/**
 * 日期类型
 *
 * @author  Promsing(张有博)
 * @since  2022/12/31 - 19:27
 * @version 1.0.0
 */
@WebServlet("/f03")
public class Freemarker03 extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    

        req.setAttribute("time",new Date());
        //转发给ftl文件中
        req.getRequestDispatcher("template/fmk03.ftl").forward(req,resp);
    }
}

HTML文件

<#--
    日期类型
    日期类型不能在FreeMarker页面输出,需要改成日期型或者String输出
        方式一:年月日         ?date
        方式二:时分秒         ?time
        方式三:年月日时分秒   ?datetime
        方式四:指定格式      ?string("自定义格式")
                             y:年 M:⽉ d:⽇
                             H:时 m:分 s:秒
    官网:http://freemarker.foofun.cn/ref_builtins_date.html
-->
<h2>日期类型</h2>
<h2>${time?date}</h2>
<h2>${time?time}</h2>
<h2>${time?datetime}</h2>
<h2>${time?string("yyyy年MM月dd日 HH:mm:ss")}</h2>

数值类型

Java文件

/**
 * 数值类型
 *
 * @author  Promsing(张有博)
 * @since  2022/12/31 - 19:27
 * @version 1.0.0
 */
@WebServlet("/f04")
public class Freemarker04 extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    

        req.setAttribute("age",18);
        req.setAttribute("num",10000);
        req.setAttribute("avg",0.05688);
        //转发给ftl文件中
        req.getRequestDispatcher("template/fmk04.ftl").forward(req,resp);
    }
}

HTML文件

<#--
    数值类型
    在FreeMarker中数值类型可以直接输出
        1.转字符串
            普通字符串   ?c
            货币类型字符串   ?string.currency
            百分比字符串     ?string.percent
        2.保留浮点型数值指定小数位(#表示一个小数位)
            ?string["0.##"]
     官方网址:http://freemarker.foofun.cn/ref_builtins_number.html
-->
<h2>数值类型</h2>
<h2>${age}</h2>
<h2>${num}</h2>
<h2>${avg}</h2>
<h2>普通字符串${num?c}</h2>
<h2>货币类型${num?string.currency}</h2>
<h2>百分比类型${avg?string.percent}</h2>
<h2>小数一位${avg?string["0.##"]}</h2>

字符串类型

Java文件

/**
 * 字符串类型
 *
 * @author  Promsing(张有博)
 * @since  2022/12/31 - 19:27
 * @version 1.0.0
 */
@WebServlet("/f05")
public class Freemarker05 extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    

       req.setAttribute("str","小小张自由");
       req.setAttribute("string","www.BAIDU.com");
        //转发给ftl文件中
        req.getRequestDispatcher("template/fmk05.ftl").forward(req,resp);
    }
}

HTML文件

<#--
    字符串类型
    在FreeMarker中字符串类型可以直接输出
      常用的方法如下
      1.截取字符串(左闭右开)  ?substring(start,end)
      2.首字母小写输出  ?uncap_first
      3.首字母大写输出  ?cap_first
      4.字母转小写输出  ?lower_case
      5.字母转大写输出  ?upper_case
      6.获取字符串长度  ?length
      7.是否以指定字符开头 (Boolean类型) ?starts_with("xx")?string
      8.是否以指定字符结尾 (Boolean类型) ?end_with("xx")?string
      9.获取指定字符的索引   ?index_of("xx")
      10.去除字符串前后空格  ?trim
      11.替换指定字符串      ?replace("xx","yy")

      官方文档:http://freemarker.foofun.cn/ref_builtins_string.html
-->

<h2>字符串类型</h2>
<h2>str--${str}</h2>
<h2>string--${string}</h2>
<h2>截取字符串--${str?substring(0,3)}</h2>
<h2>首字符小写--${string?uncap_first}</h2>
<h2>首字符大写--${string?cap_first}</h2>
<h2>字母转小写--${string?lower_case}</h2>
<h2>字母转大写--${string?upper_case}</h2>
<h2>获取字符串长度--${string?length}</h2>
<h2>是否以ww字符开头--${string?starts_with("ww")?string}</h2>
<h2>是否以ww字符结尾--${string?ends_with("ww")?string}</h2>
<h2>获取 . 字符的索引--${string?index_of(".")}</h2>
<h2>去除字符串前后空格--${string?trim}</h2>
<h2>替换指定字符串--${string?replace("www","zzz")}</h2>
<h2>字符串类型</h2>

sequence(序列类型)

包含数组、List集合、Set集合
Java代码

public class User {
    
    
    private int id;
    private String name;
    private int age;

    public User() {
    
    
    }

    public User(int id, String name, int age) {
    
    
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }
}


/**
 * sequence类型
 *
 * @author Promsing(张有博)
 * @version 1.0.0
 * @since 2022/12/31 - 19:27
 */
@WebServlet("/f06")
public class Freemarker06 extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //数组
        String[] strs = new String[]{
    
    "周杰伦", "林青霞", "黄飞鸿", "成龙"};
        req.setAttribute("strs", strs);
        //list集合
        List<String> citys = Arrays.asList("上海", "北京", "杭州", "深圳");
        req.setAttribute("citys", citys);

        List<User> userList = new ArrayList<>();
        userList.add(new User(1,"zhangsan",22));
        userList.add(new User(2,"lisi",28));
        userList.add(new User(3,"wangwu",34));
        userList.add(new User(4,"liuliu",44));
        req.setAttribute("userList",userList);
        //转发给ftl文件中
        req.getRequestDispatcher("template/fmk06.ftl").forward(req, resp);
    }
}

HTML文件


<#--序列类型(数组/list/set)
    通过list指令输出序列
    <#list 序列名 as 元素名>
        ${元素名}
    </#list>
    获取序列的长度                     ${元素名?size}
    获取序列元素的下标(循环内使用)       ${元素名?index}
    获取第一个元素                     ${元素名?first}
    获取最后一个元素                    ${元素名?last}

    倒序输出     序列名?reverse
    升序输出     序列名?sort
    降序输出     序列名?sort?reverse
    指定字段名排序     序列名?sort_by("字段名")
        注:一般是JavaBean集合
-->

<h2>数组、集合类型</h2>
<h2>数组</h2>
<#list strs as str>
    ${str?index}--${str}<br>
</#list>
获取序列的长度         ${strs?size} <br>
获取第一个元素       ${strs?first} <br>
获取最后一个元素       ${strs?last} <br>
<h2>--------------</h2>

<h2>集合list</h2>
<#list citys as ty>
    ${ty?index}--${ty}<br>
</#list>

<h2>集合list实体</h2>
<#list userList as user>
   编号:${user.id} &nbsp;姓名:${user.name} &nbsp;年龄:${user.age} <br>
</#list>

<#list userList?sort_by("age") as user>
    编号:${user.id} &nbsp;姓名:${user.name} &nbsp;年龄:${user.age} <br>
</#list>

Hash类型

注意key值只能是String类型,其他类型的话,根据key取value会报错
Java代码

/**
 * hash类型
 *
 * @author Promsing(张有博)
 * @version 1.0.0
 * @since 2022/12/31 - 19:27
 */
@WebServlet("/f07")
public class Freemarker07 extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //hashMap
        Map<String , User> map = new HashMap<>();
        map.put("1",new User(1,"zhangsan",22));
        map.put("2",new User(2,"lisi",28));
        map.put("3",new User(3,"wangwu",34));
        map.put("4",new User(4,"liuliu",44));
        req.setAttribute("userMap",map);


        Map<String, String> strMap = new HashMap<>();
        strMap.put("11","yi");
        strMap.put("22","er");
        strMap.put("33","san");
        req.setAttribute("strMap",strMap);
        //转发给ftl文件中
        req.getRequestDispatcher("template/fmk07.ftl").forward(req, resp);
    }
}

HTML文件

<#--
    hash类型
    key遍历输出
        经过测试只有String类型能使用这种方式
        <#list hash?keys as key>
            ${key}---${hash[key]}
        </list>
    value遍历输出
        <#list hash?values as value>
            ${value}
        </list>
-->

<h2>hash类型,key遍历输出</h2>
<h2> 经过测试只有String类型能使用这种方式{strMap[key]}</h2>
<#--获取key,value-->
<#list strMap?keys as key>
    Key: ${key} ---${strMap[key]} <br>
</#list>


<h2>hash类型,value遍历输出</h2>
<#--获取key-->
<#list userMap?values as value>
    编号:${value.id} &nbsp;姓名:${value.name} &nbsp;年龄:${value.age} <br>
</#list>


<h2>hash类型,value遍历输出</h2>
<h2>此时Key为String类型</h2>
<#list userMap?keys as key>
    Key: ${key} ---${userMap[key].name} <br>
</#list>

3.3 空值处理

FreeMarker空值、null值会报错,空字符串可以
解决办法
_FreeMarker提供两个运算符来避免空值

  1. !:指定缺失变量的默认值
    _ v a l u e ∗ ∗ ! ∗ ∗ ∗ ∗ 如 果 v a l u e 值为空,默认显示空字符 串 ∗ ∗ {value**!**} **_如果value值为空,默认显示空字符串 _** value!value值为空,默认显示空字符{value!**“默认值”} **_如果value值为空,默认显示"默认值"
  2. ??:判断变量是否存在
    如果变量存在,返回true,否则返回false
    _${ (value??)?string}
<h3>空字符串:${massage!}</h3>
<h3>默认值:${massage!"空字符串变默认值"}</h3>
<h3>判断是否存在:${(massage??)?string}</h3>

4、常用指令

4.1 assign 自定义变量指令

<#--
   自定义变量指令 assign
   使用assign指令可以创建一个新的变量,或者替换已经存在的变量
    语法:
    <#assign 变量名=>
    <#assign 变量名= 变量名=>定义多个变量
-->
<h2>自定义指令assign</h2>
<#assign str="hello">
${str}<br>

<#assign num=1,names=["zhangsan","lisi","wangwu"]>
${num} --${names?join(".")}
<br>
  
获取对象中的数据
<#assign mapData={"name":"程序员", "salary":15000}>
${mapData["name"]}
${mapData["salary"]}

4.2 if elseif 逻辑判断指令

<#--
   if,else,elseif 逻辑判断指令
   <#if condition>
          ...
   <#elseif condition>
          ...
   <#else condition>
          ...
   </#if>
   注意:condition将被计算成布尔表达式
   elseif,else指令都是可选的
-->
<h2> if,else,elseif 逻辑判断指令</h2>
<#assign score=88>
<#if  score lt 60 >
        不及格的成绩 ${score}
    <#elseif score==60>
        及格的成绩 ${score}
    <#elseif score gt 60 && score lt 80>
        优秀的成绩 ${score}
    <#else>
        接近满分的成绩 ${score}
</#if>

<h2>判断数据是否存在</h2>
<#assign list="">
<#if list??>
        list存在
    <#else>
        list不存在
</#if>

<#if set??>
        set存在
    <#else>
        set不存在
</#if>

4.3 list 遍历指令

<#--
    list指令
        格式1
            <#list sequence as item>
                ${item}
            </#list>
       格式1
            <#list sequence as item>
                ${item}
            <#else>
                当没有选项是执行else
            </#list>
-->

<#assign list=["lisi","shangsan","liuliu"]>
<h3>list指令-循环</h3>
<#list list as item>
    ${item} <br>
</#list>

<h3>if-list指令</h3>
<h3>相当于空指针判断</h3>
<#if map??>
    <#list map as mm>
        ${mm} <br>
    </#list>
</#if>

<#assign set=[]>
<h3>list-else指令</h3>
<#list set as item>
    ${item} <br>
    <#else>
    输出默认值
</#list>

4.4 marco自定义指令

<#--
    自定义指令
    1.基本使用:
        格式:
            <#macro 指令名>
                指令内容
            </#macro>
        使用:
            <@指令名></@指令名>
    2.有参数的自定义指令

    注意:指令可以多次使用
           自定义指令可以包含字符串,也可以包含内置指令
-->

声明指令<br>
<#macro address>
    河北省邯郸市大名县黄金堤乡
</#macro>
使用:
<@address></@address>
<hr>

声明 自定义有参数指令<br>
<#macro queryUserByName uname>
    通过用户名查询对象  ${uname}
</#macro>
使用:
<@queryUserByName uname="admin"></@queryUserByName>
<hr>
声明 自定义多参数指令<br>
<#macro queryUserByName2 uname upwd uphone>
    通过用户名查询对象  ${uname} -- ${upwd} --${uphone}
</#macro>
使用:
<@queryUserByName2 uname="admin" upwd="123" uphone="1508888"></@queryUserByName2>

4.5 nested 占位指令

<#--
    nested 占位指令
        nested 相当于占位符,一般结合macro指令一起使用
        (自我感觉像是传参数的自定义指令)
        可以将自定义指令中的内容通过nested指令占位,
        当使用自定义指令时,会将占位内容显示
-->

<#macro test>
    这是一个文本!!!,即将要被插--------
    <br>
    <#nested>
    <#nested>
</#macro>

<@test>这是插入的内容</@test>

4.6 import指令与include指令

** 导入指令 import**
在ftl文件中引入命名空间,可以使用引入的空间的宏
** 包含指令 include**
可以包含其他页面文件,比如说html,ftl,txt

<#--
    导入指令 import
        在ftl文件中引入命名空间,可以使用引入的空间的宏
    包含指令 include
        可以包含其他页面文件,比如说html,ftl,txt
-->
<#-- 导⼊命名空间 -->
<#import "commons.ftl" as common>
<#-- 使⽤命名空间中的指令 -->
<@common.cfb num=5></@common.cfb>


<#--包含指令(引⼊其他⻚⾯⽂件) include-->
<#--html⽂件-->
<#include "hello.html">
<#--freemarker⽂件-->
<#include "fmk12.ftl">
<#--txt⽂件-->
<#include "test.txt">


其他文件

此文件定义通用的宏 macro

<#macro cfb num>
        <#list 1..num as i>
                <#list 1..i as j>
                        ${j}*${i}=${j*i}&nbsp;
                </#list>
                <br>
        </#list>
</#macro>


<#macro zyb>
      <h2>我是张有博</h2>
</#macro>

5、页面静态化

通过上述介绍可知 Freemarker 是⼀种基于模板的、⽤来⽣成输出⽂本的通⽤⼯具,所以我们必须要定制符合⾃⼰业务的模板,然后⽣成⾃⼰的 html ⻚⾯。Freemarker 是通过freemarker.template.Configuration 这个对象对模板进⾏加载的(它也处理创建和缓存预解析模板的⼯作),然后我们通过 getTemplate ⽅法获得你想要的模板,有⼀点要记住freemarker.template.Configuration 在你整个应⽤必须保证唯⼀实例。

5.1 定义模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>页面静态化</title>
</head>
<body>
    <h2 align="center" >${title}</h2>

    <p align="center">
        新闻来源:${source} &nbsp;&nbsp;
        发布时间:${pubTime?datetime}
    </p>

    <p style="text-indent: 2em">
        ${content}
    </p>
</body>
</html>

5.2 Java文件

/**
 * 页面静态化
 *
 * @author Promsing(张有博)
 * @version 1.0.0
 * @since 2023/1/2 - 15:52
 */
@WebServlet("news")
public class NewsServlet extends HttpServlet {
    
    

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 实例化模板配置对象
        Configuration configuration = new Configuration();
        // 设置加载模板的上下⽂ 以及 设置加载模板路径(模板存放的路径)

        configuration.setServletContextForTemplateLoading(getServletContext(),"/template");
        // 设置模板的编码格式
        configuration.setDefaultEncoding("UTF-8");
        // 加载模板⽂件,获取模板对象
        Template template = configuration.getTemplate("news.ftl");
        // 设置模型数据
        Map<String,Object> map = new HashMap<>();
        map.put("title", "特别就业季:稳就业情况如何? 哪些问题待解?");
        map.put("source", "⼈⺠⽇报");
        map.put("pubTime", new Date());
        map.put("content", "中共中央政治局常务委员会近⽇召开会议强调," +
                "要有针对性地开展援企、稳岗、扩就业⼯作," +
                "做好⾼校毕业⽣、农⺠⼯等重点群体就业⼯作," +
                "积极帮助个体⼯商户纾困。疫情期间,稳就业情况如何?还有哪些问题待解?" +
                "记者采访了不同群体,记录这个特别的就业季。");
        // 获取项⽬所在的根⽬录
        String basePath = req.getServletContext().getRealPath("/");
        // 设置⻚⾯存放的⽬录
        File htmlFile = new File(basePath + "/html");
        // 判断⽬录是否存在
        if (!htmlFile.exists()) {
    
    
            // 如果⽬录不存在,则新建⽬录
            htmlFile.mkdir();
        }
        // 获取⽂件名(随机⽣成不重复的⽂件名)
        String fileName = System.currentTimeMillis() + ".html";
        // 创建html⽂件
        File file = new File(htmlFile, fileName);
        // 获取⽂件输出流
        FileWriter writer = new FileWriter(file);
        try {
    
    
            // 输出html 将模型数据填充到模板中
            template.process(map, writer);
            // 输出成功
            System.out.println("新闻创建成功!");
        } catch (TemplateException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            writer.flush();
            writer.close();
        }
    }
}

5.3 生成HTML文件

浏览器地址栏输⼊: http://localhost:端口号/news
⽣成的⽂件存放在当前项⽬的webapp⽬录下的html⽬录中。

6、运算符

6.1 算术运算符

<#--
   算术运算符
     +、-、*、/、%
     ${a1+a2},只有在{}内才能生效
-->

<#assign a1=8 a2=2>
${a1}+${a2}=${a1+a2}<br>
${a1}-${a2}=${a1-a2}<br>
${a1}*${a2}=${a1*a2}<br>
${a1}/${a2}=${a1/a2}<br>
${a1}%${a2}=${a1%a2}<br>
<!--字符串运算-->
${"hello"+","+"FreeMarker"}

6.2 逻辑运算符

<#--
   逻辑运算符
      &&、\\、!
-->

6.3 比较运算符

<#--
   比较运算符
      > (gt):大于号,推荐使用 gt
      < (lt):小于号,推荐使用 lt
      >= (gte):大于等于,推荐使用 gte
      <= (lte):小于等于,推荐使用 lte
      ==:等于
      !=:不等于
-->

6.4 空值运算符


<#--
   空值运算符
        1.??判断是否为空,返回布尔类型
          如果不为空返回false,如果为空返回true,不能直接输出
          ${(name??)?string}
        2.!: 设置默认值,如果为空,则设置默认值
          1.设置默认为空字符串    ${name!}
          2.设置默认值           ${name!'张三'}

-->

猜你喜欢

转载自blog.csdn.net/promsing/article/details/128522330
今日推荐