模板引擎Freemarker基础知识

  • Freemarker是什么

  • FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。
    • 常用的java模板引擎还有 Jsp、Freemarker、Thymeleaf 、Velocity 等。

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

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

      FreeMarker 基础指令

      map即为freemarker静态化所需要的数据模型

      List指令

      1、注释,即:<#‐‐被注释的内容‐‐>

      2、插值(Interpolation):即 ${…} 部分,freemarker会用真实的值替换 ${…} 中的内容

      3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。例如:<#list stuMap?keys as k> ,就是获取stuMap中的所有的key作为一个list集合,然后取list集合中的一个key赋值给k.

      4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。

      解释Demo:

      <#list stus as stu>
              <tr>
                  <td>${stu_index + 1}</td>
                  <td>${stu.name}</td>
                  <td>${stu.age}</td>
                  <td>${stu.mondy}</td>
              </tr>
          </#list>

      其中:

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

    • 遍历Map数据

    • 姓名:${stuMap['stu1'].name}<br/>
      或者
    • 姓名:${stuMap.stu1.name}<br/>

      其中stuMap是一个map集合,stu1是map集合中的一个元素对象,name是stu1的一个属性,如果采用list集合去遍历的话,第二种方法就不适用了。

    • if指令

    • if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出。
      例如:
    • <td <#if stu.name =='小明'>style="background:red;"</#if>>${stu.name}</td>

      意思就是如果stu的姓名是小明的话,就加背景色

    • 其它指令

    • 运算符

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

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

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

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

      空值处理

      1、判断某变量是否存在使用 “??” 用法为:variable??,如果该变量存在,返回true,否则返回false

      例如:

    • <#if stus??>
          <#list stus as stu>
           ......    
          </#list>
          </#if>

      意思就是:如果stus集合不为空就遍历

      2、缺失变量默认值使用 “!” 使用!要以指定一个默认值,当变量为空时显示默认值。

      例: ${name!’’}表示如果name为空显示空字符串。

      如果是嵌套对象则建议使用()括起来。

      例如: ${(stu.bestFriend.name)!’’}表示,如果stu或bestFriend或name为空默认显示空字符串。

    • 内建函数

    • 内建函数语法格式: 变量+?+函数名称
      例如:
      1、和到某个集合的大小
      ${集合名?size}
      2、日期格式化
    • 显示年月日: ${today?date}
      显示时分秒:${today?time}  
      显示日期+时间:${today?datetime} <br>       
      自定义格式化:  ${today?string("yyyy年MM月")}

      3、内建函数c

      map.put(“point”, 102920122);

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

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

      ${point?c}

      4、将json字符串转成对象

      一个例子:

      其中用到了 assign标签,assign的作用是定义一个变量。

    • <#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
      <#assign data=text?eval />
      开户行:${data.bank}  账号:${data.account}

      一般这种语法不会用

    • 入门Demo

      要导入的依赖

    • <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-freemarker</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
              </dependency>
              <dependency>
                  <groupId>com.squareup.okhttp3</groupId>
                  <artifactId>okhttp</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-io</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
              </dependency>
          </dependencies>

      配置文件

    • server:
        port: 8088 #服务端口
      spring:
        application:
          name: test-freemarker #服务名
        freemarker:
          cache: false #关闭模板缓存,及时改及时测
          settings:
            template_update_delay: 0 #检查模板更新延迟时间,例如设置为1秒,就是1秒后检查模板是否更新,设置为0表示立即检查,

      模型类

    • @Data
      public class Student {
          private String name;//姓名
          private int age;//年龄
          private Date birthday;//生日日期
          private Float money;//金额
          private List<Student> friends;//朋友
          private Student bestFriend;//最好的朋友
      }

      创建模板

    • <!DOCTYPE html>
      <html>
      <head>
          <meta charset="utf‐8">
          <title>Hello World!</title>
      </head>
      <body>
      Hello ${name}!
      <br/>
      遍历数据模型中的list学生信息,(数据模型中的名称为stus)
      <table>
          <tr>
              <td>序号</td>
              <td>姓名</td>
              <td>年龄</td>
              <td>金额</td>
              <td>出生日期</td>
          </tr>
          <#--判断list集合是否为空,为空就不执行-->
          <#if stus??>
              <#list stus as stu>
              <tr>
                  <td>${stu_index+1}</td>
                  <td <#if stu.name == '小明'>style="background-color: cyan" </#if>>${stu.name}</td>
                  <td>${stu.age}</td>
                  <td <#if (stu.money > 300)>style="background: red" </#if>>${stu.money}</td>
                  <td>${stu.birthday?date}</td>
              </tr>
          </#list>
          </#if>
      </table>
      <br/>
      学生的个数:${stus?size}
      <br/>
      遍历数据模型中的stuMap(map数据),第一种方法,在中括号中填写map的key;第二种方法,在map后面直接加 "."
      <br/>
      姓名:${(stuMap['stu1'].name)!''}<br/><#--如果stu1用户存在就显示,否则就显示一个空字符串-->
      年龄:${(stuMap['stu1'].age)!''}<br/>
      金额:${(stuMap['stu1'].money)!''}<br/>
      姓名:${(stuMap.stu1.name)!''}<br/>
      遍历map中的key,用list标签,stuMap?key就是一个key列表(是一个list)<br/>
      <#list stuMap?keys as k>
          姓名:${stuMap[k].name}<br/>
          年龄:${stuMap[k].age}<br/>
      </#list>
      <br/>
      <#--?c的作用就是去掉每三位数字一个分隔符-->
      ${point?c}
      <br/>
      <#assign text="{'bank':'工商银行','account':'101920201920212'}" />
      <#--?eval的作用就是把text的json字符串转为Java对象-->
      <#assign data=text?eval />
      开户行:${data.bank} 账号:${data.account}
      </body>
      </html>

      接口数据

    • @RequestMapping("/test1")
          public String test1(Map<String, Object> map){
              //map就是freemarker模板所使用的数据
              map.put("name", "好好学习!");
      
              Student stu1 = new Student();
              stu1.setName("小明");
              stu1.setAge(18);
              stu1.setMoney(1000.86f);
              stu1.setBirthday(new Date());
              Student stu2 = new Student();
              stu2.setName("小红");
              stu2.setMoney(200.1f);
              stu2.setAge(19);
              stu2.setBirthday(new Date());
              List<Student> friends = new ArrayList<>();
              friends.add(stu1);
              stu2.setFriends(friends);
              stu2.setBestFriend(stu1);
              List<Student> stus = new ArrayList<>();
              stus.add(stu1);
              stus.add(stu2);
              //向数据模型放数据
              map.put("stus",stus);
              //准备map数据
              HashMap<String,Student> stuMap = new HashMap<>();
              stuMap.put("stu1",stu1);
              stuMap.put("stu2",stu2);
              //向数据模型放数据
              map.put("stu1",stu1);
              //向数据模型放数据
              map.put("stuMap",stuMap);
      
              map.put("point",102920122);
      
              //返回freemarker模板的位置,基于resources/templates路径的,freemarker会从resources/templates下面找
              return "test1";//这个只能是不带后缀的文件名
          }

      使用模板文件静态化

    • public void getGenerateTest() throws IOException, TemplateException {
              //获取配置
              Configuration configuration = new Configuration(Configuration.getVersion());
      
              //获取模板
              //获取模板路径
              String path = this.getClass().getResource("/").getPath();
              configuration.setDirectoryForTemplateLoading(new File(path + "/templates/"));
              //获取模板文件
              Template template = configuration.getTemplate("test1.ftl");
      
              //获取数据模型
              Map modelMap = getModelMap();
              //静态化,生成html页面
              String templateIntoString = FreeMarkerTemplateUtils.processTemplateIntoString(template, modelMap);
             // System.out.println(templateIntoString);
              InputStream inputStream = IOUtils.toInputStream(templateIntoString);
              FileOutputStream outputStream = new FileOutputStream(new File("D:/else/test.html"));
              IOUtils.copy(inputStream,outputStream);
              //关流
              inputStream.close();
              outputStream.close();
      
          }

      使用模板字符串静态化

    • public void getGenerateTestByString() throws IOException, TemplateException {
              //获取模板字符串
               String templateString="" +
                  "<html>\n" +
                  "    <head></head>\n" +
                  "    <body>\n" +
                  "    名称:${name}\n" +
                  "    </body>\n" +
                  "</html>";
              //生成模板
              Configuration configuration = new Configuration(Configuration.getVersion());
              //获取模板加载器
              StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
              //把字符串模板放入模板加载器中
              stringTemplateLoader.putTemplate("template", templateString);
              //设置模板加载器
              configuration.setTemplateLoader(stringTemplateLoader);
              //转成模板
              Template template = configuration.getTemplate("template", "utf-8");
      
              //获取模型数据
              Map modelMap = getModelMap();
      
              //静态化
              String templateIntoString = FreeMarkerTemplateUtils.processTemplateIntoString(template, modelMap);
              InputStream inputStream = IOUtils.toInputStream(templateIntoString);
              FileOutputStream fileOutputStream = new FileOutputStream(new File("D:/else/test2.html"));
              IOUtils.copy(inputStream, fileOutputStream);
              fileOutputStream.close();
              inputStream.close();
      
          }