• Freemarker快速入门(SpringBoot版)


    Freemarker快速入门

    在这里插入图片描述

    1、Freemarker概述

    • Freemarker是什么

      Freemarker是一个强大的 Java 模板引擎,它允许开发人员将展示层与应用逻辑分离。通过提供一种灵活高效的方式,在运行时生成动态内容。Freemarker模板使用类似HTML的语法,但包含可以在运行时填充数据的占位符和控制结构。这使得创建动态网页、电子邮件和其他文本文档非常简单。

      Freemarker支持条件语句、循环、宏等各种功能,并且可以与不同的数据模型和框架集成。它在Java Web开发中被广泛应用于生成HTML视图、电子邮件模板和其他文本输出。

      个人理解:Freemarker是类似于JSP的模板引擎,只要我们理解了什么是模板引擎就知道Freemarker大概是一个什么东西了

      PS:如果你学习过 JSP 或者 Thmeleaf,我相信你对模板引擎不会陌生,关于 JSP 和 Thmeleaf 的详细介绍可以参考这两篇文章:JSP速通Thmeleaf快速入门(Spring整合版)

    • 什么是模板引擎

      模板引擎是一种用于生成动态内容的工具或框架。它将应用程序的逻辑和数据与视图分离,允许开发人员使用预定义的模板来定义页面、电子邮件、报表等的结构和布局。通过在模板中使用占位符、条件语句、循环等控制结构,模板引擎可以动态地将数据填充到模板中,生成最终的输出结果。这样的方法可以提高开发效率,减少代码重复,并使前后端工作更加分离。模板引擎通常支持多种语法和标记,可以集成到不同的编程语言和框架中,以满足各种应用场景的需求。

      个人理解:模板引擎就是一类特殊的HTML页面,它将数据和页面骨架(HTML)、样式(CSS)与数据进行了分离,数据是需要我们动态填充(模板引擎这个概念很类似与前端的组件,模板引擎是一种后端的页面渲染技术)

    • 模板引擎的作用是什么

      个人理解:模板引擎的主要作用是将逻辑和数据进行分离,提高复用性,相当于是给我们一个模板,我们只需要往这个模板中填充数据即可

      • 分离视图和业务逻辑:模板引擎使开发人员能够将视图(如网页、电子邮件等)的结构和布局与应用程序的业务逻辑分开。这样,设计师和前端开发人员可以专注于界面设计,而后端开发人员可以专注于业务逻辑的实现。
      • 提高开发效率:使用模板引擎可以减少编写重复代码的工作量。开发人员只需定义一次模板,并根据需要填充不同的数据即可生成多个相似但内容不同的输出结果。这样可以节省时间并提高开发效率。
      • 支持可维护性和扩展性:通过将视图和业务逻辑分离,模板引擎使代码更易于维护和修改。开发人员可以更轻松地修改UI布局或样式,而无需修改底层的业务逻辑。此外,模板引擎还提供了灵活的控制结构和标记,使开发人员可以根据需要进行定制和扩展。
      • 支持多平台和多语言:模板引擎通常支持多种编程语言和框架,可以适用于不同的开发环境和应用场景。这使得开发人员能够在不同的项目中重用他们的模板知识和经验。
    • 常见的模板引擎有哪些

      模板引擎说明
      JSPJsp 为 Servlet 专用,不能单独进行使用
      Thymeleaf新技术,功能较为强大,但是执行的效率比较低
      FreeMarker性能好,强大的模板语言、轻量
      VelocityVelocity从2010年更新完 2.0 版本后,7年没有更新。Spring Boot 官方在 1.4 版本后对此也不在支持
    • Freemarker的组成

      • 模板:模板是FreeMarker的核心组件,它定义了要生成的输出内容的结构和格式。模板使用FreeMarker的模板语法编写,可以包含变量、表达式、标签和指令等。

        模板的组成:

        • 文本:文本会照着原样来输出。
        • 插值:这部分的输出会被计算的值来替换。插值由 ${ and } 所分隔(或者 #{ and },这种风格已经不建议再使用了;点击查看更多)。
        • FTL 标签:FTL标签和HTML标签很相似,但是它们却是给FreeMarker的指示, 而且不会打印在输出内容中。
        • 注释:注释和HTML的注释也很相似,但它们是由 <#---->来分隔的。注释会被FreeMarker直接忽略, 更不会在输出内容中显示。
      • 模板解析器:模板解析器负责将模板文件解析为抽象语法树(AST),以便进行后续的处理和渲染。模板解析器将模板语法解析为相应的AST节点,表示模板的结构和内容。

      • 数据模型:数据模型是FreeMarker所使用的数据源,它包含了要在模板中展示的数据。数据模型可以是任意的Java对象,例如POJO、Map、List等。在模板中可以通过表达式访问数据模型的属性和方法。

      • 标签和指令:FreeMarker提供了一些内置的标签和指令,用于控制模板的渲染和逻辑处理。标签和指令可以包含条件判断、循环控制、变量赋值等逻辑,以及数据展示和格式化等操作。

      • 模板渲染引擎:模板渲染引擎是FreeMarker的核心引擎,负责将模板和数据结合起来进行渲染。模板渲染引擎根据AST节点和数据模型,生成最终的输出内容。

      除了上述组件之外,FreeMarker还提供了一些辅助类和工具,用于扩展和定制模板引擎的功能。例如,提供了各种自定义标签和函数的扩展点,以及模板缓存和预编译等性能优化功能。

    2、Freemarker初体验

    由于平常大部分都是基于SpringBoot框架进行开发的,这里我就不基于Spring学习Freemarker了,直接基于SpringBoot演示Freemarker的使用

    创建SpringBoot项目
    添加依赖
    编写模板
    编写配置文件
    编写Controller
    测试
    • Step1:创建SpringBoot项目

      image-20231017223637599

    • Step2:添加依赖

              
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-freemarkerartifactId>
              dependency>
      
              
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-webartifactId>
              dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • Step3:编写模板

      src/main/resources/templates目录下创建 index.ftl 文件

      根据 Spring Boot 约定,模板文件默认是放在 src/main/resources/templates 目录下,如果你想要自定义模板文件的位置,可以通过template-loader-path配置属性进行设置。

      DOCTYPE html>
      <html>
          <head>
              <meta charset="UTF-8">
              <title>Freemarkertitle>
          head>
          <body>
              Hello ${title}!
          body>
      html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • Step4:编写配置文件

      spring:
        freemarker:
          suffix: .ftl # 模板文件的后缀名,默认为 .ftl
      #    content-type: text/html # 响应的文档类型,默认为 text/html
      #    charset: UTF-8 # 页面的编码,默认为 UTF-8
          cache: false # 是否启用页面缓存,默认为 true。如果设置为 false,则每次请求都会重新解析模板文件
      #    template-loader-path: classpath:/templates/ # 模板文件的路径,默认为 classpath:/templates/。可以根据实际情况进行配置
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      注意:suffix虽然有默认值 .ftl,但是仍然需要手动配置,否则无法成功响应模板

    • Step5:编写Controller

      package com.ghp.freemarker.controller;
      
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.servlet.ModelAndView;
      
      /**
       * @author ghp
       * @title
       * @description
       */
      @Controller
      @RequestMapping("/page")
      public class PageController {
      
          @GetMapping("/index1")
          public String toIndex1(){
              return "index1";
          }
      
          @GetMapping("/index2")
          public ModelAndView toIndex2 () {
              ModelAndView modelAndView = new ModelAndView("index2");
              modelAndView.addObject("title", "Freemarker");
              return modelAndView;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28

      注意:不要使用@RestController注解表示响应模板视图的 Controller 类,因为使用@RestController注解会将 Controller 类标识为一个 RESTful API 控制器,它主要用于返回数据或处理 API 请求,并不会返回视图页面

    • Step5:测试

      现在可以在浏览器上分别访问 http://localhost:8080/page/index1 和 http://localhost:8080/page/index2

      image-20231017223447188

    3、数据类型

    Freemarker是一个模板引擎,它主要用于生成动态的文本输出。它并没有特定的数据类型,而是使用 Java 对象作为模板的数据模型,所以这里的数据类型本质还是 Java 中的数据类型,本节主要讲解 Freemarker 中如何操作不同的数据类型

    3.0 常见操作符

    操作符描述
    ?c格式化对象
    ?string将对象转成字符串
    ?string.currency将对象转成货币类型的字符串
    ?string.percent将对象转成百分比类型的字符串
    ?string["0.#"]将数字转成字符串,并四舍五入保留一位小数
    ?time将日期按照“时:分:秒”输出
    ?date将日期按照“年-月-日”输出
    ?datetime将日期按照“年-月-日 时:分:秒”输出
    ?html对字字符串进行转移输出
    ?esc对字字符串进行转移输出
    ?reverse反转结果
    ?sort排序
    ?index获取当前遍历元素的索引
    ?sort_by按照某一个字段进行排序

    3.1 数值类型

    • 设置值
    • 获取值

    3.2 字符串类型

    • 设置值
    • 获取值

    3.3 布尔类型

    • 设置值
    • 获取值

    3.4 日期类型

    • 设置值
    • 获取值

    3.5 序列类型

    • 设置值
    • 获取值

    3.6 哈希类型

    • 设置值
    • 获取值

    这里就统一编写吧,不一个一个的CV了,太浪费时间了,直接来一个整的

        /**
         * 演示 Freemarker 中不同的数据类型的相关操作
         *
         * @return
         */
        @GetMapping("/data_type")
        public ModelAndView toDataType() {
            // 响应的模板
            ModelAndView modelAndView = new ModelAndView("data_type");
            // 响应数值类型的数据
            modelAndView.addObject("number", 666);
            // 响应字符串类型的数据
            modelAndView.addObject("string", "Freemarker");
            // 响应布尔类型的数据
            modelAndView.addObject("boolean", true);
            // 响应日期类型的数据
            modelAndView.addObject("date", new Date());
            // 响应序列类型的数据(数组、List、Set)
            modelAndView.addObject("sequence", Arrays.asList("张三", "李四", "王五"));
            // 响应哈希类型的数据
            modelAndView.addObject("hash", new HashMap<String, String>(3) {{
                put("a", "A");
                put("b", "B");
                put("c", "C");
            }});
    
            // 还可以直接使用 addAllObjects 一次性全部添加
            modelAndView.addAllObjects(new HashMap<String, Object>(6) {{
                put("number2", 3.1415926);
                put("string2", "Freemarker");
                put("boolean2", false);
                put("date2", new Date());
                put("sequence2", new String[]{"张三", "李四", "王五"});
                put("hash2", new HashMap<String, String>(3) {{
                    put("a", "A");
                    put("b", "B");
                    put("c", "C");
                }});
                User user1 = new User(1L, "张三", 19);
                User user2 = new User(2L, "李四", 20);
                User user3 = new User(3L, "李四", 21);
                ArrayList<User> userList = new ArrayList<>(3);
                Collections.addAll(userList, user1, user2, user3);
                put("userList", userList);
    
                Map<String, User> userMap = new HashMap<>(3);
                userMap.put("key1", user1);
                userMap.put("key2", user2);
                userMap.put("key3", user3);
                put("userMap", userMap);
            }});
            return modelAndView;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    image-20231018225602446

    4、运算符

    4.1 算术运算符

    
    <#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"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.2 逻辑运算符

    <#--
        逻辑运算符
         &&、||、!
    -->
    
    • 1
    • 2
    • 3
    • 4

    4.3 比较运算符

    <#--
        比较运算符
            > (gt): 大于号,推荐使用 gt
            < (lt): 小于号,推荐使用 lt
            >= (gte): 大于等于, 推荐是用 gte
            <= (lte): 小于等于,推荐使用 lte
            == : 等于
            != : 不等于
    -->
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.4 空值运算符

    <#--
        空值运算符
         1. ??:判断是否为空,返回布尔类型
            如果不为空返回 false, 如果为空返回 true,不能直接输出
           ${(name??)?string}
         2. !: 设置默认值,如果为空,则设置默认值
             1. 设置默认为空字符串:
             ${name!}
             2. 设置指定默认值
             ${name!'zhangsan'}
    -->
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5、常用指令

    5.1 assign 自定义变量指令

    使用 assign 指令你可以创建一个新的变量, 或者替换一个已经存在的变量。

    <#--
        assign 自定义变量指令
            语法:
                <#assign 变量名=>
                <#assign 变量名= 变量名=> (定义多个变量)
    -->
    <#assign str="hello">
    ${str} <br> 
    <#assign num=1 names=["zhangsan","lisi","wangwu"] >
    ${num} -- ${names?join(",")}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.2 if elseif else 逻辑判断指令

    可以使用 if , elseif 和 else 指令来条件判断是否满足某些条件。

    <h5> 2. if, else, elseif 逻辑判断指令h5>
    <#assign score = 60>
    <#if score lt 60 >
        <h6>你个小渣渣!h6>
        <#elseif score == 80>
            <h6>分不在高,及格就行!h6>
        <#elseif score gt 60 && score lt 80 >
            <h6>革命尚未成功,同志仍需努力!h6>
        <#else >
            <h6>哎哟不错哦!h6>
    #if>
    <#--判断数据是否存在-->
    <#assign list="">
    <#if list??>
        数据存在
        <#else >
            数据不存在
    #if>
    <br>
    <#if list2??>
        数据存在
    <#else >
        数据不存在
    #if>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5.3 list 遍历指令

    可以使用 list 指令来对序列进行遍历。

    <h5>3. list指令h5>
    <#assign users = ["张三","李四","王五"]>
    <#list users as user>
        ${user} |
    #list>
    <br>
    <#--判断数据不为空,再执行遍历 (如果序列不存在,直接遍历会报错)-->
    <#if users2??>
        <#list users2 as user>
            ${user}
        #list>
    #if>
    <br>
    <#-- 当序列没有数据项时,使用默认信息 -->
    <#assign users3 = []>
    <#list users3 as user>
        ${user} |
        <#else >
            用户数据不存在!
    #list>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    5.4 macro 自定义指令

    可以使用 macro 指令来自定义一些自定义指令。

    <#--
        macro 自定义指令 (宏)
            1. 基本使用
                格式:
                    <#macro 指令名>
                        指令内容
                    #macro>
                使用:
                    <@指令名>@指令名>
            2. 有参数的自定义指令
                格式:
                     <#macro 指令名 参数名1 参数名2>
                        指令内容
                    #macro>
                使用:
                    <@指令名 参数名1=参数值1 参数名2=参数值2>@指令名>
            注:
                1. 指令可以被多次使用。
                2. 自定义指令中可以包含字符串,也可包含内置指令
    -->
    <#-- 定义基本的自定义指令 -->
    <#macro address>
        © 1999–2015 The FreeMarker Project. All rights reserved. 
    #macro> 
    <#-- 使用指令 -->
    <@address>@address> <br> 
    <@address>@address> <hr> 
    <#-- 定义有参数的自定义指令 -->
    <#macro queryUserByName uname>
        通过用户名查询用户信息 - ${uname} 
    #macro> 
    <#-- 使用指令,并传递参数 -->
    <@queryUserByName uname="admin">@queryUserByName> <br> 
    <#-- 定义有多个参数的自定义指令 -->
    <#macro queryUserByParams uname uage>
        通过多个餐宿查询用户信息 - ${uname} - ${uage} #macro> 
    <#-- 使用指令,并传递多个参数 -->
    <@queryUserByParams uname="admin" uage=18>@queryUserByParams> <br> 
    <hr>
    <#-- 自定义指令中包含内置指令 -->
    <#macro cfb>
        <#list 1..9 as i>
            <#list 1..i as j>
               ${j}*${i}=${j*i} 
            #list>
            <br>
        #list> 
    #macro> 
    <@cfb>@cfb> 
    <@cfb>@cfb> 
    <#-- 动态数据 -->
    <#macro cfb2 num>
        <#list 1..num as i>
            <#list 1..i as j>
               ${j}*${i}=${j*i} 
            #list>
            <br>
        #list> 
    #macro> 
    <@cfb2 num=5>@cfb2>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    5.5 nested 占位指令

    nested 指令执行自定义指令开始和结束标签中间的模板片段。嵌套的片段可以包含模板中任意合法的内容。

    <#--
        nested 占位指令
            nested 相当于占位符,一般结合macro指令一起使用。
            可以将自定义指令中的内容通过nested指令占位,当使用自定义指令时,会将占位内容显示。
    -->
    <#macro test>
        这是一段文本!
        <#nested>
        <#nested> 
    #macro> 
    <@test><h4>这是文本后面的内容!h4>@test>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.6 import 导入指令

    import 指令可以引入一个库。也就是说,它创建一个新的命名空间, 然后在那个命名空间中执行给定路径的模

    板。可以使用引入的空间中的指令。

    1)创建commons.ftl文件

    <#macro cfb>
        <#list 1..9 as i>
            <#list 1..i as j>
               ${j}*${i}=${j*i} 
            #list>
            <br>
        #list> 
    #macro>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在其他ftl页面中通过import导入commons.ftl的命名空间,使用该命名空间中的指令

    2)创建test.ftl文件

    <#-- 导入命名空间 -->
    <#import "commons.ftl" as common> 
    <#-- 使用命名空间中的指令 -->
    <@common.cfb>@common.cfb>
    
    • 1
    • 2
    • 3
    • 4

    3)创建Controller

    package org.example.controller;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @author ghp
     * @description Freemarker常用指令
     * @date 2022/12/20 14:05
     */
    @WebServlet("/f04")
    public class FreeMarker04 extends HttpServlet {
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
    //        请求转发到指定的页面,
            req.getRequestDispatcher("template/test.ftl").forward(req, resp);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5.7 include 包含指令

    可以使用 include 指令在你的模板中插入另外一个 FreeMarker 模板文件 。 被包含模板的输出格式是在 include

    标签出现的位置插入的。 被包含的文件和包含它的模板共享变量,就像是被复制粘贴进去的一样。

    <#--包含指令(引入其他页面文件) include-->
    <#--html文件-->
    <#include "test.html"> 
    <#--freemarker文件-->
    <#include "test.ftl"> 
    <#--text文件-->
    <#include "test.txt">
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    参考资料

  • 相关阅读:
    Vue数据代理,事件处理,计算属性
    shell 内置命令
    关于conda、虚拟环境、镜像通道、pip、pycharm解释器配置的一些总结
    AcWing 836. 合并集合 —— 递推递归一起食用效果更佳哦~
    C#中.NET 7.0 Windows窗体应用通过EF访问新建数据库
    bootstrap-validator实现验证表单
    Apache顶级项目Ranger和Alluxio的最佳实践(附教程)
    hp-unix 数据库服务起不来
    三维点云的格网(体素)组织
    人工智能 框架 paddlepaddle 飞桨 使用指南& 使用例子 线性回归模型demo 详解
  • 原文地址:https://blog.csdn.net/qq_66345100/article/details/133916789