SpringBoot结合FreeMarker视图渲染的实现

目录
  • 一、FreeMarker 简介
    • 1.1 什么是FreeMarker?
    • 1.2 Freemarker模板组成部分
    • 1.3 为什么要使用FreeMarker
  • 二、Springboot集成FreeMarker
    • 2.1 配置
    • 2.2 数据类型
      • 2.2.1 字符串
      • 2.2.2 数值
      • 2.2.3 布尔值
      • 2.2.4 日期
    • 2.3 常见指令  
      • 2.3.2 assign
      • 2.3.3 include
  • 三、常见指令实现增删改查(综合案例)⭐
    • 3.1 后端
      • 3.2 前端
        • 3.3 效果展示

        一、FreeMarker 简介

        1.1 什么是FreeMarker?

        FreeMarker是一款 模板引擎 ,它允许开发人员使用模板和数据来生成输出文本,如HTML网页、电子邮件、配置文件和源代码等。它是一个Java类库,可以被嵌入到开发人员所创建的产品中。开发人员可以使用FreeMarker来动态生成和渲染视图,以便将数据呈现给最终用户

        https://freemarker.apache.org/

        下面是一些关键点:

        • 模板引擎: FreeMarker是一种模板引擎,允许开发者定义模板文件,其中包含要动态填充的占位符或表达式。这些模板文件可以包含HTML、XML、文本等。

        • 动态内容生成: FreeMarker的主要用途是根据模板和数据生成最终的输出内容。模板中的占位符将被实际的数据替代,从而生成动态的、个性化的输出。

        • 嵌入式组件: FreeMarker是一个Java类库,可以轻松地集成到Java应用程序中。这使得开发人员可以在他们的Java应用中使用FreeMarker来处理视图层的动态内容生成。

        • 面向程序员: FreeMarker通常被程序员用于构建动态的用户界面或生成其他类型的文本输出。它不直接面向最终用户,而是提供给开发人员一个工具,使他们能够以更灵活和动态的方式生成内容。

        1.2 Freemarker模板组成部分

        FreeMarker模板文件主要由如下4个部分组成:

        • 文本:直接输出的部分

        • 注释:使用 <#– … –> 格式做注释,里面内容不会输出

        • 插值:即 ${…} 或 #{…} 格式的部分,类似于占位符,将使用数据模型中的部分替代输出

        • FTL指令:即FreeMarker指令,全称是:FreeMarker Template Language,和HTML标记类似,但名字前加#予以区分,不会输出

        1.3 为什么要使用FreeMarker

        FreeMarker在Spring Boot中被广泛使用的原因与其特性和与Spring Boot的集成有关。

        • 轻量级: FreeMarker相对轻量,不引入过多的依赖,易于集成和使用。

        • 与Spring框架整合良好: FreeMarker与Spring框架集成良好,通过Spring Boot的自动配置,可以很容易地配置FreeMarker作为模板引擎。

        • Spring Boot 默认支持: Spring Boot对多个模板引擎提供了自动配置支持,包括FreeMarker。因此,Spring Boot项目中使用FreeMarker非常方便,只需在依赖中引入相应的starter即可。

        • 开箱即用: FreeMarker可以作为Spring Boot的一部分,无需额外的配置。这使得开发者能够快速启动项目并使用FreeMarker构建视图。

        • 简化模板文件的构建: FreeMarker的模板语法相对简洁,可以更容易地与后端Java代码交互。在Spring Boot项目中,Java对象的数据可以直接在FreeMarker模板中使用。

        FreeMarker的诞生是为了取代JSP。虽然JSP功能强大,可以写Java代码实现复杂的逻辑处理,但是页面会有大量业务逻辑,不利于维护和阅读,更不利于前后台分工,容易破坏MVC结构,所以舍弃JSP,选择使用FreeMarker是大势所趋。

        二、Springboot集成FreeMarker

        2.1 配置

        1、配置pom.xml,引入依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        2、添加application.yml配置:

          freemarker:
            # 设置模板后缀名
            suffix: .ftl
            # 设置文档类型
            content-type: text/html
            # 设置页面编码格式
            charset: UTF-8
            # 设置页面缓存
            cache: false
            # 设置ftl文件路径
            template-loader-path: classpath:/templates
            # 设置静态文件路径,js,css等
            mvc:
              static-path-pattern: /static/**

        3、新建模板文件(.ftl)

        在resources/templates目录新建一个.ftl文件

        第一创建是没有这个文件类型的选项的,所以我们要定义个.ftl格式的文件:

        settings –> Editor –> File and Code Templates 

        该内容模板于html差不多的,最后在创建文件的时候就有个一个.ftl格式的文件选项

        index.ftl:

        <!DOCTYPE html>
        <html lang="zh">
        <head>
            <meta charset="UTF-8">
            <title>Freemarker</title>
        </head>
        <body>
        <h1>Hello FreeMarker!!!</h1>
        </body>
        </html>

        Controller:

        package com.ycxw.boot.controller;
        
        import org.springframework.stereotype.Controller;
        import org.springframework.web.bind.annotation.RequestMapping;
        
        /**
         * @author 云村小威
         * @create 2023-12-13 18:57
         */
        @Controller
        public class IndexController {
        
            @RequestMapping("/")
            public String home(){
                return "index";
            }
        }
        

        一个基本的数据显示就成功了

        2.2 数据类型

        2.2.1 字符串

        在文本中确定字符串值的方法是看双引号,比如: "some text",或单引号,比如: 'some text'。这两种形式是等同的。 如果文本自身包含用于字符引用的引号 ( " 或 ')或反斜杠时, 应该在它们的前面再加一个反斜杠;这就是转义。 转义允许直接在文本中输入任何字符, 也包括换行。

        ${"It's \"quoted\" and this is a backslash: \\"}

        字符串类型处理:

        方法 含义
        ?substring(start,end) 截取字符串(左闭右开)
        ?uncap_first 首字母小写输出
        ?cap_first 首字母大写输出
        ?lower_case 字母转小写输出
        ?upper_case 字母转大写输出
        ?length 获取字符串长度
        ?starts_with("xx")?string 是否以指定字符开头(boolean类型)
        ?ends_with("xx")?string 是否以指定字符结尾(boolean类型)
        ?index_of("xx") 获取指定字符的索引
        ?trim 去除字符串前后空格
        ?replace("xx","xx") 替换指定字符串

        字符串空值情况处理:

        FreeMarker 的变量必须赋值,否则就会抛出异常。而对于 FreeMarker 来说,null 值和不存在的变量是完全一样的,因为 FreeMarker 无法理解 null 值。

        <#-- 如果值不存在,直接输出会报错 -->
        <#--${str}-->
        <#-- 使用!,当值不存在时,默认显示空字符串 -->
        ${str!}
        <#-- 使用!"xx",当值不存在时,默认显示指定字符串 -->
        ${str!"这是一个默认值"}
        <#-- 使用??,判断字符串是否为空;返回布尔类型。如果想要输出,需要将布尔类型转换成字符串 -->
        ${(str??)?string}

        注意事项:

        使用??或?starts_with("xx"),判断字符串是否为空或指定字符开头;返回布尔类型。如果想要输出,需要将布尔类型转换成字符串在后面添加 ?c or ?string

        例如:

        ${"嗨害嗨"?starts_with("我")}
        ${str??}

        不进行转换则会报错

        2.2.2 数值

        输入不带引号的数字就可以直接指定一个数字, 必须使用点作为小数的分隔符而不能是其他的分组分隔符。

        ${0.45}
        ${18}
        <#-- 将数值转换成字符串输出 -->
        ${1000?c} 
        <#-- 将数值转换成货币类型的字符串输出 -->
        ${1000?string.currency} 
        <#-- 将数值转换成百分比类型的字符串输出 -->
        ${0.45?string.percent} 
        <#-- 将浮点型数值保留指定小数位输出 (##表示保留两位小数) -->
        ${0.45723123?string["0.##"]} 

        2.2.3 布尔值

        直接写 true 或者 false 就表示一个布尔值了,不需使用引号。

        在freemarker中布尔类型不能直接输出;如果输出要先转成字符串

        ${flag?c}
        ${flag?string}
        ${flag?string("yes","no")}

        2.2.4 日期

        日期变量可以存储和日期/时间相关的数据。

        在freemarker中日期类型不能直接输出;如果输出要先转成日期型或字符串

        日期格式输出:

        输出方式 说明
        ?date 年月日
        ?time 时分秒
        ?datetime 年月日时分秒
        ?string("自定义格式") 指定格式
        <#-- 输出日期格式 -->
        ${createDate?date}
        <#-- 输出时间格式 -->
        ${createDate?time}
        <#-- 输出日期时间格式 -->
        ${createDate?datetime}
        <#-- 输出格式化日期格式 -->
        ${createDate?string("yyyy年MM月dd日 HH时mm分ss秒")}

        注意事项:

        在.ftl文件中不能直接创建时间需要通过后端传入时间进行显示(替换掉createDate)

        2.3 常见指令  

        2.3.2 assign

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

        案例演示:

        <#-- 创建一个str的变量 -->
        <#assign str="hello">
        <#-- 输出str -->
        ${str} 
        <#-- 一次创建多个变量 -->
        <#assign num=1 names=["嗨","害","嗨"] >
        ${num} and ${names?join(",")}

        2.3.3 include

        可以使用它在你的模板中插入另外一个 FreeMarker 模板文件 (由 path 参数指定)

        该标签主要用于引入其他资源,简化数据

        首先创建一个公共页面定义一个变量存储本地访问路径

        在index.ftl中进行引入调用src数值

        <#-- 引入公共页面 -->
        <#include "common.ftl">
        <a href="${src}/xxx" rel="external nofollow" ></a>

        还有如:if/elseif/else、list指令在下面的综合案例中进行演示👇

        三、常见指令实现增删改查(综合案例)⭐

        3.1 后端

        BookMapper.xml

            <select id="list" resultMap="BaseResultMap" resultType="com.ycxw.boot.entity.Book">
                select
                <include refid="Base_Column_List"/>
                from t_book
                where 1=1
                <if test="bookname !=null and bookname !=''">
                    and bookname like CONCAT('%',#{bookname},'%')
                </if>
            </select>

        在此添加了模糊查询的方法,其他的增删改都是自动生成。

        BookMapper.java

        package com.ycxw.boot.mapper;
        
        import com.ycxw.boot.entity.Book;
        import org.apache.ibatis.annotations.Param;
        import org.springframework.stereotype.Repository;
        
        import java.util.List;
        
        /**
        * @author 云村小威
        * @description 针对表【t_book(书本信息表)】的数据库操作Mapper
        * @createDate 2023-12-12 14:50:45
        * @Entity com.ycxw.boot.entity.Book
        */
        @Repository
        public interface BookMapper {
        
            List<Book> list(@Param("bookname")String bookname);
        
            ...其他方法
        }
        

        BookService.java

        package com.ycxw.boot.service;
        
        import com.ycxw.boot.entity.Book;
        import org.apache.ibatis.annotations.Param;
        
        import java.util.List;
        
        /**
         * @author 云村小威
         * @create 2023-12-12 15:11
         */
        public interface BookService {
            List<Book> list(@Param("bookname")String bookname);
        
            int insert(Book record);
        
            int deleteByPrimaryKey(Long id);
        
            int updateByPrimaryKey(Book record);
        }
        

        BookServiceImpl.java

        package com.ycxw.boot.service.impl;
        
        import com.ycxw.boot.entity.Book;
        import com.ycxw.boot.mapper.BookMapper;
        import com.ycxw.boot.service.BookService;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Service;
        
        import java.util.List;
        
        /**
         * @author 云村小威
         * @create 2023-12-12 15:12
         */
        @Service
        public class BookServiceImpl implements BookService {
        
            @Autowired
            private BookMapper bookMapper;
        
            @Override
            public List<Book> list(String bookname) {
                return bookMapper.list(bookname);
            }
        
            @Override
            public int insert(Book record) {
                return bookMapper.insert(record);
            }
        
            @Override
            public int deleteByPrimaryKey(Long id) {
                return bookMapper.deleteByPrimaryKey(id);
            }
        
            @Override
            public int updateByPrimaryKey(Book record) {
                return bookMapper.updateByPrimaryKey(record);
            }
        }
        

        IndexController.java

        package com.ycxw.boot.controller;
        
        import com.ycxw.boot.entity.Book;
        import com.ycxw.boot.service.BookService;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Controller;
        import org.springframework.ui.Model;
        import org.springframework.web.bind.annotation.*;
        
        import java.util.List;
        
        /**
         * @author 云村小威
         * @create 2023-12-13 18:57
         */
        @Controller
        public class IndexController {
            @Autowired
            private BookService bookService;
        
            /**
             * 查询所有
             *
             * @param model
             * @return
             */
            @RequestMapping("/")
            public String home(Model model) {
                List<Book> list = bookService.list(null);
                model.addAttribute("book", list);
                return "index";
            }
        
            /**
             * 新增
             *
             * @param book
             * @return
             */
            @PostMapping("/add")
            public String add(@RequestBody Book book) {
                bookService.insert(book);
                return "redirect:/";
            }
        
            /**
             * 删除
             *
             * @param book
             * @return
             */
            @RequestMapping("/del")
            public String del(Book book) {
                bookService.deleteByPrimaryKey(book.getId().longValue());
                return "redirect:/";
            }
        
            /**
             * 修改
             *
             * @param book
             * @return
             */
            @PutMapping("/edit")
            public String edit(@RequestBody Book book) {
                bookService.updateByPrimaryKey(book);
                return "redirect:/";
            }
        
            /**
             * 模糊查询
             *
             * @param bookname
             * @param model
             * @return
             */
            @RequestMapping(value = "/search", method = RequestMethod.POST)
            public String search(@RequestParam("bookname") String bookname, Model model) {
                // 在这里使用 bookname 进行相关的处理
                List<Book> list = bookService.list(bookname);
                model.addAttribute("book", list);
                return "index"; // 返回搜索结果页面
            }
        
        
        }
        

        3.2 前端

        common.ftl (公共资源)

        <#--引入当前项目的运行路径-->
        <#assign src="${springMacroRequestContext.contextPath}">
        <#--引入样式与脚本-->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="external nofollow" >
        <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
        <!-- 引入Bootstrap的JavaScript文件(需要在jQuery之后引入) -->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js"></script>
        

        index.ftl(自定义引擎模版)

        <!DOCTYPE html>
        <html lang="zh">
        <head>
            <meta charset="UTF-8">
            <title>FreeMarker</title>
            <#--引入公共模块-->
            <#include "common.ftl">
        </head>
        <body>
        <div class="container mt-5" id="body">
            <div class="row justify-content-center">
                <div class="col-3">
                    <input type="text" class="form-control" id="input" placeholder="请输入书籍名称"
                           style="width: 300px"/>
                </div>
                <div class="col-2">
                    <button type="submit" class="btn btn-dark mb-3" onclick="query()">搜索</button>
                    <button type="button" class="btn btn-outline-dark mb-3" data-bs-toggle="modal"
                            data-bs-target="#exampleModal">
                        新增书籍
                    </button>
                </div>
        
            </div>
            <div class="row">
                <table class="table table-hover" style="width: 65%;margin: auto">
                    <thead class="table-dark">
                    <tr>
                        <th>序号</th>
                        <th>书籍名称</th>
                        <th>价格</th>
                        <th>类型</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    <#if book??>
                        <#list book as b>
                            <tr>
                                <td>${b.id}</td>
                                <td>${b.bookname}</td>
                                <td>${b.price}</td>
                                <td>${b.booktype}</td>
                                <td>
                                    <a href="${src}/del?id=${b.id}" rel="external nofollow" >删除</a>
                                    <a href="javascript:void(0);" rel="external nofollow" 
                                       onclick="editModal('${b.id}', '${b.bookname}', '${b.booktype}', ${b.price})"
                                       data-bs-toggle="modal" data-bs-target="#editModal">修改</a>
                                </td>
                            </tr>
                        </#list>
                    </#if>
                    </tbody>
                </table>
            </div>
        </div>
        <!-- 新增弹窗 -->
        <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="exampleModalLabel">新增书籍</h5>
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                    </div>
                    <div class="modal-body">
                        <!-- 表单 -->
                        <form>
                            <div class="mb-3">
                                <label for="title" class="form-label">书籍名称</label>
                                <input type="text" class="form-control" id="name" placeholder="请输入标题">
                            </div>
                            <div class="mb-3">
                                <label for="author" class="form-label">类型</label>
                                <select class="form-select" id="bookType" aria-label="Default select example">
                                    <option selected>请选择书籍类型</option>
                                    <option value="动作">动作</option>
                                    <option value="冒险">冒险</option>
                                    <option value="文学">文学</option>
                                </select>
                            </div>
                            <div class="mb-3">
                                <label for="price" class="form-label">价格</label>
                                <input type="number" class="form-control" id="price" placeholder="请输入价格">
                            </div>
                            <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal" aria-label="Close">取消
                            </button>
                            <button type="button" class="btn btn-dark" onclick="add()">提交</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
        <!-- 修改弹窗 -->
        <div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="exampleModalLabel">修改书籍</h5>
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                    </div>
                    <div class="modal-body">
                        <!-- 表单 -->
                        <form>
                            <div class="mb-3">
                                <label for="id" class="form-label">书籍编号</label>
                                <input type="text" class="form-control" id="id" disabled>
                            </div>
                            <div class="mb-3">
                                <label for="title" class="form-label">书籍名称</label>
                                <input type="text" class="form-control" id="name_edit" placeholder="请输入标题">
                            </div>
                            <div class="mb-3">
                                <label for="author" class="form-label">类型</label>
                                <select class="form-select" id="type_edit" aria-label="Default select example">
                                    <option selected>请选择书籍类型</option>
                                    <option value="动作">动作</option>
                                    <option value="冒险">冒险</option>
                                    <option value="文学">文学</option>
                                </select>
                            </div>
                            <div class="mb-3">
                                <label for="price" class="form-label">价格</label>
                                <input type="number" class="form-control" id="price_edit" placeholder="请输入价格">
                            </div>
                            <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal" aria-label="Close">取消
                            </button>
                            <button type="button" class="btn btn-dark" onclick="edit()">确认修改</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
        </body>
        <script>
            /*模糊查询*/
            function query() {
                //获取输入值
                var name = $("#input").val();
                // 发送AJAX请求
                $.ajax({
                    url: "${src}/search",
                    type: "POST",
                    contentType: "application/x-www-form-urlencoded",
                    data: {"bookname": name},
                    success: function (data) {
                        //更新整个页面元素
                        $("#body").html(data);
                    }
                });
            }
        
            /*新增方法*/
            function add() {
                // 获取表单数据
                var name = $("#name").val();
                var type = $("#bookType").val();
                var price = $("#price").val();
                // 创建一个对象来存储表单数据
                var book = {
                    bookname: name,
                    booktype: type,
                    price: price
                };
                // 发送AJAX请求
                $.ajax({
                    url: "${src}/add",
                    type: "POST",
                    contentType: "application/json",
                    data: JSON.stringify(book),
                    success: function (res) {
                        console.log(res);
                        // 请求成功处理逻辑
                        alert("书籍添加成功!");
                        // 关闭弹窗
                        var modal = $("#exampleModal");
                        modal.modal("hide");
                        // 刷新页面
                        location.reload();
                    }
                });
            }
        
            /* 修改方法(回显数据) */
            function editModal(id, name, type, price) {
                // 将数据设置到弹窗表单中
                $("#id").val(id);
                $("#name_edit").val(name);
                $("#type_edit").val(type);
                $("#price_edit").val(price);
        
                // 显示修改弹窗
                $("#editModal").modal("show");
            }
        
            /*修改方法*/
            function edit() {
                // 获取表单数据
                var id = $("#id").val();
                var name = $("#name_edit").val();
                var type = $("#type_edit").val();
                var price = $("#price_edit").val();
                // 创建一个对象来存储表单数据
                var book = {
                    id: id,
                    bookname: name,
                    booktype: type,
                    price: price
                };
                // 发送AJAX请求
                $.ajax({
                    url: "${src}/edit",
                    type: "PUT",
                    contentType: "application/json",
                    data: JSON.stringify(book),
                    success: function (res) {
                        console.log(res);
                        // 请求成功处理逻辑
                        alert("书籍修改成功!");
                        // 关闭弹窗
                        var modal = $("#editModal");
                        modal.modal("hide");
                        // 刷新页面
                        location.reload();
                    }
                });
            }
        </script>
        </html>

        3.3 效果展示

        3.3.1 新增功能

        3.3.2 修改功能

        3.3.3 查询功能

        3.3.4 删除功能

        到此这篇关于SpringBoot结合FreeMarker视图渲染的实现的文章就介绍到这了,更多相关SpringBoot FreeMarker视图渲染内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

        您可能感兴趣的文章:

        • Springboot整合Freemarker的实现详细过程
        • springboot整合freemarker代码自动生成器
        • Springboot整合freemarker和相应的语法详解
        • 如何在SpringBoot+Freemarker中获取项目根目录
        • Springboot整合FreeMarker的实现示例
        • SpringBoot整合Freemarker的基本步骤
        • springboot整合freemarker的踩坑及解决
        • SpringBoot整合freemarker实现代码生成器
        • SpringBoot整合FreeMarker的过程详解

        Published by

        风君子

        独自遨游何稽首 揭天掀地慰生平