第16章 Springboot整合Thymeleaf引擎开发Web页面

1,208 阅读8分钟

任务描述

任务要求

使用IDEA开发工具构建一个项目多模块工程。study-springboot-chapter10学习关于Springboot集成Thymeleaf模板引擎知识点

  1. 基于study-springboot工程,复制study-springboot-chapter00标准项目,坐标groupId(com.cbitedu)、artifactId(study-springboot-chapter10),其他默认
  2. 继承study-springboot工程依赖
  3. 引入框架Thymeleaf实现前端页面开发

任务收获

  1. Spring Boot中整合Thymeleaf
  2. 学会使用JUnit完成单元测试
  3. Thymeleaf模板引擎使用
  4. 掌握web开发原理
  5. 熟悉SpringMVC的基础配置

任务准备

环境要求

  1. JDK1.8+
  2. MySQL8.0.27+
  3. Maven 3.6.1+
  4. IDEA/VSCode

数据库准备

创建数据库platform,并创建用户表和初始化用户表数据。

-- ----------------------------
-- Table structure for t_book
-- ----------------------------
DROP TABLE IF EXISTS `t_book`;
CREATE TABLE `t_book`  (
  `id` int NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '书名',
  `writer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '作者',
  `introduction` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '简介',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_book
-- ----------------------------
INSERT INTO `t_book` VALUES (1, '高等数学', '张海', '专升本必备教材');

工程目录要求

study-springboot-chapter10

任务实施

1. 前言

模板引擎这个词,咋听起来,有点高大上的意味。

实际上,模板引擎是非常平易近人的技术。譬如大家可能都比较熟悉的 JSP ,就是一种比较典型的模板引擎。

当浏览器将请求抛给控制器,控制器处理好数据后,就跳转 JSP 等模板引擎页面。注意在跳转的同时,还会将数据组装好,也交给模板引擎处理。

模板引擎会根据数据,和模板引擎的规则,动态生成 HTML 页面,最后返回给浏览器显示。

2. 模板引擎使用场景

我们使用 Spring Boot 开发 Web 项目,大体上有两种方式。

第一种方式,是后端服务化的方式,也是当前的主流方式。前端是静态的 HTML 页面,通过 Ajax 请求 Spring Boot 的后端接口。 Spring Boot 返回数据一般采用 JSON 格式,前端接收后将数据显示。

第二种方式,是采取模板引擎的方式。前端的请求,到达 Spring Boot 的控制器后,控制器处理请求,然后将返回数据交给模板引擎。模板引擎负责根据数据生成 HTML 页面,最后将 HTML 返回给浏览器。

我个人比较推荐第一种方式,说一下该方式的几个优点:

  • 便于分工协作:后端可以按自己的进度开发接口,前端可以开发页面,需要的时候直接调用后端 API ;
  • 便于项目拓展:比如前期是做的网站,后续要加一个 APP ,后端接口可以直接复用;
  • 降低服务端压力:后端只提供数据,一部分业务逻辑在前端处理了。服务端要做的事情少了,自然压力就小。

模板引擎开发的页面,对搜索引擎 SEO 比较友好;还有就是简单的页面,如果用模板引擎开发速度比较快,毕竟模板化的方法,目的就是减少重复提高效率。

3. Spring Boot 中常用的模板引擎

Spring Boot 支持的模板引擎种类很多,常见的有 FreeMarker 、 Thymeleaf 、 JSP 。因为这些模板引擎使用的用户都不少,所以我们逐一介绍下其实现过程。

至于孰优孰劣,请各位看官自行评价。正所谓:尺有所短,寸有所长,各取所爱,万物生长!

4. 整体流程说明

在做一个项目或一个模块的时候,不要一开始就动手写代码,最好是谋定而后动。设计和实现思路很重要(重点的事情说三遍

我们作为程序员,实际上是整个程序世界的总指挥。应该先整体规划,再实现局部。这种总分型的开发方法便于我们理顺思路,提高编码效率!编码如行军,先了解地形,设定整体作战计划,再派兵执行任务

好的,我们来思考下,实现商品浏览项目实例的整体流程:

可以看到,我们是先建立了控制器方法和页面,再去实现其中的具体细节。这样可以让我们的思维保持连贯性和整体性,在做一些页面和方法较多的项目时,会感觉更加顺畅。

模板引擎Thymeleaf

Thymeleaf是本文我们将具体介绍使用的模板引擎。它是一个XML/XHTML/HTML5模板引擎,可用于Web与非Web环境中的应用开发。

Thymeleaf提供了一个用于整合Spring MVC的可选模块,在应用开发中,你可以使用Thymeleaf来完全代替JSP或其他模板引擎,如Velocity、FreeMarker等。Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。

动手试一下

第一步:基于标准Springboot工程模板study-springboot-chapter00,在pom.xml中加入所需的模板引擎模块,比如使用thymeleaf的话,只需要引入下面依赖:

 <!-- Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>		
<!-- ThymeLeaf依赖 -->
	<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
	</dependency>

第二步:在application.yml文件中注入Thymeleaf相关配置,默认只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染。

spring:
  thymeleaf:
    #prefix:指定模板所在的目录
    prefix: classpath:/templates/
    #check-tempate-location: 检查模板路径是否存在
    check-template-location: true
    #cache: 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器,线上设置为true,可以提高性能。
    cache: false
    #suffix 配置模板后缀名
    suffix: .html
    encoding: UTF-8
    mode: HTML5

 

书籍类Book,服务类 BookService ,这两个类与上面没有区别直接放出代码。

实例:

package com.cbitedu.springboot.entity;

import java.io.Serializable;

/**
 * Book 实体类
 *
 * Created by cbitedu
 */
public class Book implements Serializable {

    /**
     * 编号
     */
    private Long id;

    /**
     * 书名
     */
    private String name;

    /**
     * 作者
     */
    private String writer;

    /**
     * 简介
     */
    private String introduction;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getWriter() {
        return writer;
    }

    public void setWriter(String writer) {
        this.writer = writer;
    }

    public String getIntroduction() {
        return introduction;
    }

    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }
}

实例:

接口和实现类

package com.cbitedu.springboot.service;

import com.cbitedu.springboot.entity.Book;

import java.util.List;

/**
 * Book 业务接口层
 *
 * Created by cbitedu
 */
public interface BookService {
    /**
     * 获取所有 Book
     */
    List<Book> findAll();

    /**
     * 新增 Book
     *
     * @param book {@link Book}
     */
    int insertByBook(Book book);

    /**
     * 删除 Book
     *
     * @param id 编号
     */
    int delete(Long id);

    /**
     * 获取 Book
     *
     * @param id 编号
     */
    Book findById(Long id);
}
package com.cbitedu.springboot.service.impl;

import com.cbitedu.springboot.entity.Book;
import com.cbitedu.springboot.service.BookService;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;


/**
 * Book 业务层实现
 *
 * Created by cbitedu
 */
@Service
public class BookServiceImpl implements BookService {

    private final JdbcTemplate jdbcTemplate;

    BookServiceImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public List<Book> findAll() {
        List<Book> users = jdbcTemplate.query("select * from t_book  ", (resultSet, i) -> {
            Book book = new Book();
            book.setId(resultSet.getLong("id"));
            book.setName(resultSet.getString("name"));
            book.setWriter(resultSet.getString("writer"));
            book.setIntroduction(resultSet.getString("introduction"));
            return book;
        });
        return users;
    }

    @Override
    public int insertByBook(Book book) {
        return jdbcTemplate.update("insert into t_book(id, name,writer,introduction) values(?, ?,?,?)",book.getId(),book.getName(), book.getWriter(),book.getIntroduction());
    }
    @Override
    public int delete(Long id) {
        return jdbcTemplate.update("delete from t_book where id = ?", id);
    }

    @Override
    public Book findById(Long id) {
        return (Book) jdbcTemplate.queryForObject("SELECT * FROM t_book WHERE id=?", new Object[]{id}, new BeanPropertyRowMapper(Book.class));
    }
}

创建控制器方法,指向书籍页面

创建控制器类, BookController , Thymeleaf 直接使用 HTML 作为模板页面,故代码如下:

实例:

package com.cbitedu.springboot.controller;

import com.cbitedu.springboot.entity.Book;
import com.cbitedu.springboot.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.ArrayList;
import java.util.List;

/**
 * Book 控制层
 *定义服务接口来向thymeleaf模板传递数据,接口定义时需要注意:
 * * 使用@Controller注解而不是@RestController,因此@RestController会将结果解析后直接展示字符串内容,而不能根据字符串获取对应的模板名称
 * 服务接口需要加入Model对象作为参数,并使用Model对象来绑定需要传递的数据,以便在thymeleaf中使用
 * Created by cbitedu
 */
@Controller
@RequestMapping(value = "/book")
public class BookController {
    @Autowired
    BookService bookService;
    /**
     * 用户信息列表页
     */
    @RequestMapping("/list")
    public ModelAndView getBookList(){
        List bookList=new ArrayList();
        bookList=bookService.findAll();
        return new ModelAndView("/booklist").addObject("booklist",bookList);

    }
}

创建书籍列表页面

我们在 resource/templates 目录下新建书籍列表页面 booklist.html ,先不必实现具体功能,代码如下:

实例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <script type="text/javascript" th:src="@{https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js}"></script>
    <link th:href="@{https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css}" rel="stylesheet"/>
    <link th:href="@{/css/default.css}" rel="stylesheet"/>
    <link rel="icon" th:href="@{/images/favicon.ico}" type="image/x-icon"/>
    <meta charset="UTF-8"/>
    <title>书籍列表</title>
</head>

<body>

<div class="contentDiv">

    <h5> spring boot集成thymeleaf</h5>

    <table class="table11">
        <legend>
            <strong>书籍列表</strong>
        </legend>
        <thead>
        <tr>
            <td>书籍编号</td>
            <td>书名</td>
            <td>作者</td>
            <td>简介</td>
            <td>管理</td>
        </tr>
        </thead>
        <tbody>
        <tr th:each="book : ${booklist}">
            <th scope="row" th:text="${book.id}"></th>
            <td><a th:href="@{/book/update/{bookId}(bookId=${book.id})}" th:text="${book.name}"></a></td>
            <td th:text="${book.writer}"></td>
            <td th:text="${book.introduction}"></td>
            <td><a class="btn btn-danger" th:href="@{/book/delete/{bookId}(bookId=${book.id})}">删除</a></td>
        </tr>
        </tbody>
    </table>

    <div><a class="btn btn-primary" href="/book/create" role="button">新增书籍</a></div>
</div>

</body>
</html>

此时我们启动项目,然后访问 http://127.0.0.1:81/book/list ,即可显示对应页面内容。

 

到此,大家基本上也能发现,这两种方式除了模板页面文件内容不同,其他地方基本都是一模一样的。

也就是说,模板引擎主要负责通过一些模板标签,将控制器返回的数据解析为网页。

Thymeleaf的配置参数

如有需要修改默认配置的时候,只需复制下面要修改的属性到application.properties中,并修改成需要的值:

举几个我们常用的配置内容:

Q:不想每次修改页面都重启

A:修改spring.thymeleaf.cache参数,设置为false

Q:不想使用template目录存放模板文件

A:修改spring.thymeleaf.prefix参数,设置为你想放置模板文件的目录

Q:不想使用index作为模板文件的扩展名

A:修改spring.thymeleaf.suffix参数,设置为你想用的扩展名

Q:HTML5的严格校验很烦人

A:修改spring.thymeleaf.mode参数,设置为LEGACYHTML5

实验实训

1、重点学习Thymeleaf模板其他标签的使用

2、引入另外Freemarker引擎开发web