freemarker 介绍

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

​ 模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。

常用的java模板引擎还有哪些?(面试)

Jsp、Freemarker、Thymeleaf 、Velocity 等。

1.Jsp 为 Servlet 专用,不能单独进行使用。

2.Thymeleaf 为新技术,功能较为强大,但是执行的效率比较低。

3.Velocity从2010年更新完 2.0 版本后,便没有在更新。Spring Boot 官方在 1.4 版本后对此也不在支持,虽然 Velocity 在 2017 年版本得到迭代,但为时已晚。

模板+数据模型=输出

freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也可以生成其它格式的文本文件)

1、数据模型

数据模型在java中可以是基本类型也可以List、Map、Pojo等复杂类型。

2、来自官方的例子

环境搭建&&快速入门

freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。

需要创建Spring Boot+Freemarker工程用于测试模板。

在线预览:freemarker作为springmvc一种页面。在浏览器展示

浏览器发起请求---->SpringMVC---->执行Controller----->定义返回值(模板地址)---->模板中绑定数据---->浏览器展示

创建测试工程

这里我就简单介绍一下,要想详细了解大家可以进我发官网去看看

导入依赖

    <!-- 添加springboot 父类依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <dependencies>
        <!--添加 spring boot web 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</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>
        <!-- lombok使用 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>
        <!-- apache 对 java io 的封装工具库 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>

配置application.yml配置

spring:
  freemarker:
    #开启 freemarker 功能
    enabled: true
    #关闭模板缓存,方便测试
    cache: false
    settings:
      template_update_delay: 0
    #页面模板后缀名
    suffix: .ftl
    charset: UTF-8
    #页面模板位置(默认为 classpath:/templates/)
    template-loader-path: classpath:/templates/
  resources:
    #关闭项目中的静态资源映射(static、resources文件夹下的资源)
    add-mappings: false

创建模型类;

package com.xuecheng.freemarker.entity;

import lombok.Data;
import lombok.ToString;
import java.util.Date;
import java.util.List;

@Data
@ToString
public class Student {
    private String name;//姓名
    private int age;//年龄
    private Date birthday;//生日
    private Float money;//钱包
}

模拟controller层:

package com.xuecheng.test.freemarker.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

@Controller
public class HelloController {

    @GetMapping("basic")
    public String test(Model model) {
        //1.纯文本形式的参数
        model.addAttribute("name", "freemarker");
        //2.实体类相关的参数
        Student student = new Student();
        student.setName("小明");
        student.setAge(18);
        model.addAttribute("stu", student);
        return "01-basic";
    }
}

创建模板

在resources下创建templates,此目录为freemarker的默认模板存放目录
在templates下创建模板文件 01-basic.ftl ,模板中的插值表达式最终会被freemarker替换成具体的数据。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<b>普通文本 String 展示: Hello ${name}</b><br><br>
 <br>
<hr>
<b>对象Student中的数据展示:${stu}</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
</body>
</html>

支持EL表达式

在这里插入图片描述


页面上的指令,基本语法规则

基础语法种类:

1.注释,即<#--  -->,介于其之间的内容会被freemarker忽略
<#--我是一个freemarker注释-->

在这里插入图片描述

2、插值(Interpolation):即 ${..} 部分,freemarker会用真实的值代替${..}
  Hello ${name}

在这里插入图片描述

3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。
  <#>FTL指令</#> 
 @GetMapping("/list")
    public String list(Model model) {
        //响应数据
        //2.1 小强对象模型数据
        Student stu1 = new Student();
        stu1.setName("小强");
        stu1.setAge(18);
        stu1.setMoney(1000.86f);

        //2.2 小红对象模型数据
        Student stu2 = new Student();
        stu2.setName("小红");
        stu2.setMoney(200.1f);
        stu2.setAge(19);

        //2.3 将两个对象模型数据存放到List集合中
        List<Student> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);
        model.addAttribute("stus",stus);

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>

<b>展示list中的stu数据:</b>
<br>
<br>
<#--list 数据的展示-->
<b>展示list中的stu数据:</b>
<#list stus as s>
    name:${s.name},
    age:${s.age},
    money:${s.money},
</#list>

<hr>
</body>
</html>

在这里插入图片描述

  4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容.
  
     <#--freemarker中的普通文本-->
     我是一个普通的文本

在这里插入图片描述


集合指令(List和Map)

 @GetMapping("/list")
    public String list(Model model) {
        //响应数据
        //2.1 小强对象模型数据
        Student stu1 = new Student();
        stu1.setName("小强");
        stu1.setAge(18);
        stu1.setMoney(1000.86f);

        //2.2 小红对象模型数据
        Student stu2 = new Student();
        stu2.setName("小红");
        stu2.setMoney(200.1f);
        stu2.setAge(19);

        //2.3 将两个对象模型数据存放到List集合中
        List<Student> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);
        model.addAttribute("stus",stus);


        //3. 创建Map数据
        Map<String,Student> stuMap = new HashMap<>();
        stuMap.put("stu1",stu1);
        stuMap.put("stu2",stu2);
        model.addAttribute("stuMap", stuMap);

        //返回模板地址
        return "02-collection";
    }
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>

<b>展示list中的stu数据:</b>
<br>
<br>
<#--list 数据的展示-->
<b>展示list中的stu数据:</b>
<#list stus as s>
    name:${s.name},
    age:${s.age},
    money:${s.money},
</#list>
<hr>
<b>展示map中的数据:</b>
<#--获取map中某个key下的数据:map.key.属性-->
stu1的name:${stuMap.stu1.name}<br>
stu2的name:${stuMap.stu2.name}<br>
<#--遍历map集合,获取map中的所有数据
    语法:[map]?keys as key  -- 循环map集合获取获取每个key
-->
<hr>
遍历map集合 <br>
<#--<#list stuMap?keys as key>-->
<#--    ${stuMap[key].name} --  ${stuMap[key].age} <br>-->
<#--</#list>-->

<#list stuMap? keys as key>
    ${stuMap[key].name} ---- ${stuMap[key].age}-----${stuMap[key].money}<br>
</#list>
</body>
</html>

在这里插入图片描述

${k_index}:
	index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始

在这里插入图片描述
在这里插入图片描述


if指令
if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出。

<#--格式-->
<#if condition>
      ...
    <#elseif condition2>
      ...
    <#elseif condition3>
      ...
    ...
    <#else>
      ...
</#if>
package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;

import java.util.*;

@Controller
public class HelloController {

    @GetMapping("basic")
    public String test(Model model) {
        //1.纯文本形式的参数
        model.addAttribute("name", "freemarker");
        //2.实体类相关的参数
        Student student = new Student();
        student.setName("小明");
        student.setAge(18);
        model.addAttribute("stu", student);
        return "01-basic";
    }


    @GetMapping("/list")
    public String list(Model model) {
        //响应数据
        //2.1 小强对象模型数据
        Student stu1 = new Student();
        stu1.setName("小强");
        stu1.setAge(18);
        stu1.setMoney(1000.86f);

        //2.2 小红对象模型数据
        Student stu2 = new Student();
        stu2.setName("小红");
        stu2.setMoney(200.1f);
        stu2.setAge(19);

        //2.3 将两个对象模型数据存放到List集合中
        List<Student> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);
        model.addAttribute("stus",stus);


        //3. 创建Map数据
        Map<String,Student> stuMap = new HashMap<>();
        stuMap.put("stu1",stu1);
        stuMap.put("stu2",stu2);
        model.addAttribute("stuMap", stuMap);

        //返回模板地址
        return "03-if";
    }
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<#list stus as s>
    <#if s.name='小红'>
        <b style="color: red">
            ${s.name}<br>
        </b>
        <#elseif s.name='小绿'>
          <b style="color: green">
              ${s.name}<br>
          </b>
          <#--类似于swich中的default-->
            <#else>
        ${s.name}<br>
    </#if>
</#list>
</body>
</html>

在这里插入图片描述


空值处理:
Freemarker 在显示数据时,如果遇到了null或空,Freemarker就会在显示页面中报出错误信息:null or missing,

Freemarker中需要对空值间判断,判断后的数据就不会显示错误信息。

判断某变量是否存在使用 “??”
如:为防止stus为空报错可以加上判断如下:
    <#if stus??>
    <#list stus as stu>
    	......
    </#list>
    </#if>
缺失变量默认值使用 “!**
使用!要以指定一个默认值,当变量为空时显示默认值
如:${name!''}表示如果name为空显示空字符串。

实际例子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<#list stus as s>
    <#if s??>
        <#if s.name='小红'>
            <b style="color: red">
                ${s.name}<br>
                ${s.birthday!'2022-8-11'}<br>
            </b>
        <#elseif s.name='小绿'>
            <b style="color: green">
                ${s.name}<br>
                ${s.birthday!'2022-8-11'}<br>
            </b>
        <#else>
            ${s.name}<br>
            ${s.birthday!'2022-8-11'}<br>
        </#if>
    </#if>
</#list>
</body>
</html>

在这里插入图片描述


内建函数
内建函数语法格式: 变量+?+函数名称

  • 某个集合的大小
<hr>
 集合大小:${stus?size}<br>

在这里插入图片描述
在这里插入图片描述

  • 日期格式化

    显示年月日: ${today?date}
    显示时分秒:${today?time}
    显示日期+时间:${today?datetime}
    自定义格式化: ${today?string("yyyy年MM月")}
    在这里插入图片描述

 Date date = new Date();
        model.addAttribute("today",date);
<hr>
日期格式![在这里插入图片描述](https://img-blog.csdnimg.cn/1bde3ff81828446ab31fa2797001ac11.png)
<br>
显示年月日:${today?date}<br>
显示时分秒:${today?time}<br>
显示日期+时间:${today?datetime}<br>
自定义格式化:${today?string("yyyy/MM月dd日")}

在这里插入图片描述

  • 内函数c
model.addAttribute("point", 102920122);

point是数字型,使用${point}会显示这个数字的值,每三位使用逗号分隔。

如果不想显示为每三位分隔的数字,可以使用c函数将数字型转成字符串输出

在这里插入图片描述

 model.addAttribute("point", 102920122);
<hr>
内函数:<br>
有c:${point?c}<br>
无c:${point}

在这里插入图片描述


静态化测试(练习)

之前的测试都是SpringMVC将Freemarker作为视图解析器(ViewReporter)来集成到项目中,工作中,有的时候需要使用Freemarker原生Api来生成静态内容并使用文件来存储静态化后的内容,下面一起来学习下原生Api生成文本文件。

使用freemarker原生Api将页面生成html文件,本节测试html文件生成的方法:

1、根据模板文件生成html文件的字符串

​	模板文件 + 数据模型 = 静态文件 

2、根据模板字符串生成静态文件

​    模板字符串 + 数据模型 = 静态文件 

GeneratorHtml.class

package com.example;

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;


@SpringBootTest
public class GeneratorHtml {

    @Test
    public void testHtml() throws Exception {
        //1、创建配置类,指定版本信息
        Configuration configuration = new Configuration(Configuration.getVersion());
        //2、指定模板,字符编码
        String path = this.getClass().getResource("/templates").getPath();
        configuration.setDirectoryForTemplateLoading(new File(path)); //指定模板文件的文件夹路径
        configuration.setDefaultEncoding("utf-8");
        //3、获取模板文件
        Template template = configuration.getTemplate("01-basic.ftl");
        //4、构造数据,必须是一个Map集合
        Map data = getData();
        //5、根据数据 + 模板文件进行数据填充,得到字符串
        String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
        //6、将静态化字符串写入到指定文件下
        InputStream inputStream = IOUtils.toInputStream(content); //字符串转化为输入流
        FileOutputStream outputStream = new FileOutputStream(new File("e:/index.html")); //创建文件
        IOUtils.copy(inputStream, outputStream); //输入流写入文件
    }

    private Map getData() {
        Map map = new HashMap();
        map.put("name","zjf");
        Student student  = new Student();
        student.setName("王者荣耀");
        student.setAge(12);
        map.put("student",student);
        return map;
    }
}

01-basic.ftl

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<#--我是一个freemarker注释-->
<b>普通文本 String 展示:</b><br><br>
Hello  ${name1!""}<br>
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${student.name}<br/>
年龄:${student.age}
<hr>


</body>
</html>

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐