Thymeleaf是官方推荐的显示引擎,这篇文章主要介绍怎么让spring boot整合Thymeleaf.  它是一个适用于Web和独立环境的现代服务器端Java模板引擎。

Thymeleaf的主要目标是给开发工作流程带来优雅的自然模板 - 可以在浏览器中正确显示的HTML,也可以用作静态原型,从而在开发团队中实现更强大的协作。通过Spring Framework模块,与喜欢的工具的集成,Thymeleaf是HTML5 JVM Web开发的理想选择。

1.自然模板官方示例

用Thymeleaf编写的HTML模板看起来和HTML一样工作

<table>
<thead>
<tr>
<th th:text="#{msgs.headers.name}">Name</th>
<th th:text="#{msgs.headers.price}">Price</th>
</tr>
</thead>
<tbody>
<tr th:each="prod: ${allProducts}">
<td th:text="${prod.name}">Oranges</td>
<td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
</tr>
</tbody>
</table>

2.项目结构

2.1 主要功能还是添加一本书,查看一本书的明细,以及返回所有的书籍。这次项目中用到两个数据表。

CREATE TABLE `book` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`Category` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`Price` decimal(18,2) NOT NULL,
`Publish_Date` date NOT NULL,
`Poster` varchar(128) NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=87 DEFAULT CHARSET=utf8 CREATE TABLE `author` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`phone` varchar(16) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
`book_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

2.2 和系列二中的示例几乎一样,多了一个templates文件夹用来存放thymeleaf文件。

需要在application.properties文件中添加以下内容:

spring.thymeleaf.cache=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html

2.3 项目用到的技术如下,

  • Spring-Data-JPA
  • H5
  • Bootstrap
  • Thymeleaf
  • 数据库依旧是MySQL

2.4 项目逻辑架构图

2.5 完整的pom.xml如下,

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- springboot,jpa 整合包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql 驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--根据MySQL server 版本实际情况变动 -->
<version>8.0.17</version><!--$NO-MVN-MAN-VER$ -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

3.后端服务层

3.1 com.example.demo.model包中新建Author,Book,BookViewModel类,

@Entity
public class Author implements Serializable {
private static final long serialVersionUID = -3523362375538074534L;
@Id
private Integer id;
private String name;
private String phone;
private String email;
private Integer bookId;
} //省略getter & setter方法 @Entity
public class Book implements Serializable {
private static final long serialVersionUID = -3123479062966697145L;
@Id
private Integer id;
private String name;
private String category;
private Double price;
private Date publish_date;
private String poster;
} //省略getter & setter方法 public class BookViewModel {
private Book book;
private Author author;
} //省略getter & setter方法

3.2com.example.demo.store包中,新建AuthorRepository,BookRepository接口

public interface BookRepository extends JpaRepository<Book,Integer> {
@Query(value = "SELECT * FROM book order by Id desc", nativeQuery = true)
List<Book> findBooks();
} public interface AuthorRepository extends JpaRepository<Author, Integer> {
Author findByBookId(Integer bookId);
}

3.3com.example.demo.controller包中创建BookController类,

@Controller
@RequestMapping(path = "/book")
public class BookController { @Autowired
private BookRepository bookRepository; @Autowired
private AuthorRepository authorRepository; @Value("${upload.path}")
private String filePath; @GetMapping(path="/add")
public ModelAndView add() {
ModelAndView modelAndView = new ModelAndView("add");
return modelAndView;
} @PostMapping(path = "/save")
public String save(HttpServletRequest request, @RequestParam("inputPoster") MultipartFile inputPoster) { String tempPath = filePath;
String name = request.getParameter("inputName");
String category = request.getParameter("categorySelect");
Double price = Double.valueOf(request.getParameter("inputPrice"));
// Give today as a default date
Date publishDate = new Date();
String temp = request.getParameter("inputDate");
DateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
// Test only
try {
publishDate = formatDate.parse(temp);
} catch (ParseException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
// Handle uploading picture
String fileName = inputPoster.getOriginalFilename();
String suffixName = fileName.substring(fileName.lastIndexOf('.'));
fileName = UUID.randomUUID() + suffixName;
String posterPath = "image/" + fileName;
Book book = new Book();
book.setId(0);
book.setName(name);
book.setCategory(category);
book.setPrice(price);
book.setPublish_date(publishDate);
book.setPoster(posterPath); tempPath += fileName;
try {
inputPoster.transferTo(new File(tempPath));
bookRepository.save(book);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "redirect:/book/all";
} @GetMapping(path = "/all")
public ModelAndView findAll() {
ModelAndView modelAndView = new ModelAndView("home");
Iterable<Book> books = bookRepository.findBooks();
modelAndView.addObject("bookList", books);
return modelAndView;
} @GetMapping(path="view/{id}")
public ModelAndView view(@PathVariable Integer id) {
ModelAndView mv = new ModelAndView("view");
Optional<Book> optional = bookRepository.findById(id);
Book book = optional.orElseGet(Book::new);
Author author = authorRepository.findByBookId(id);
if(author == null) {
author = new Author();
}
BookViewModel bookViewModel = new BookViewModel();
bookViewModel.setAuthor(author);
bookViewModel.setBook(book);
mv.addObject("bookView",bookViewModel);
return mv; }
}

3.4com.example.demo.config 包中的WebConfig类和示例二中完全一样,此处省略不提。

4.前端显示层

在templates文件夹里面添加三个文件,分别是home.html, add.html, view.html.

4.1 home.html

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8"></meta>
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"></meta> <!-- Bootstrap CSS -->
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"></link> <title>Index Page</title>
</head>
<body>
<div class="container">
<h1>Springboot进阶系列三</h1> <!-- Content here -->
<div class="table-responsive">
<table class="table table-striped table-sm" id="books">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<tr th:each="book : ${bookList}">
<td th:text="${book.id}"></td>
<td th:text="${book.name}"></td>
<td th:text="${book.category}"></td>
<td th:text="${book.price}"></td>
<td><a class="btn btn-outline-primary"
th:href="@{/book/view/{id}(id=${book.id})}" role="button">View</a>
<a class="btn btn-outline-primary"
th:href="@{/book/edit/{id}(id=${book.id})}" role="button">Edit</a>
<a class="btn btn-outline-primary"
th:href="@{/book/delete/{id}(id=${book.id})}" role="button">Delete</a></td>
</tr>
</tbody>
</table>
</div> <a href="/book/add" class="btn btn-outline-primary" role="button"
aria-pressed="true">Add Book</a>
</div>
</body>
</html>

4.2 add.html

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8"></meta>
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"></meta> <!-- Bootstrap CSS -->
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"></link> <title>Add Book</title>
</head>
<body>
<div class="container">
<h1>Springboot进阶系列三</h1>
<!-- Content here -->
<form action="/book/save" method="POST" enctype="multipart/form-data">
<div class="form-group row">
<label for="inputName" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="inputName"
placeholder="Name">
</div>
</div>
<div class="form-group row">
<label for="categorySelect" class="col-sm-2 col-form-label">Category</label>
<div class="col-sm-10">
<select class="form-control" name="categorySelect">
<option>武侠</option>
<option>历史</option>
<option>军事</option>
<option>国学</option>
<option>投资</option>
<option>管理</option>
<option>传记</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="inputPrice" class="col-sm-2 col-form-label">Price</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="inputPrice"
placeholder="Price">
</div>
</div>
<div class="form-group row">
<label for="inputDate" class="col-sm-2 col-form-label">Publish
Date</label>
<div class="col-sm-10">
<input type="date" class="form-control" name="inputDate" />
</div>
</div>
<div class="form-group row">
<label for="inputPoster" class="col-sm-2 col-form-label">Poster</label>
<div class="col-sm-10">
<input type="file" class="form-control" name="inputPoster" />
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-outline-primary">Save</button>
</div>
</div>
</form>
</div>
</body>
</html>

4.3 view.html

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8"></meta>
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"></meta> <!-- Bootstrap CSS -->
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"></link> <title>View Book</title>
</head>
<body>
<div class="container">
<h1>Springboot进阶系列三</h1>
<!-- Content here --> <div class="form-group row">
<label class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:value="${bookView.Book.name}">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Category</label>
<div class="col-sm-10">
<select class="form-control" th:value="${bookView.Book.category}">
<option>武侠</option>
<option>历史</option>
<option>军事</option>
<option>国学</option>
<option>投资</option>
<option>管理</option>
<option>传记</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Price</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:value="${bookView.Book.price}">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Publish
Date</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:value="${#dates.format(bookView.Book.publish_date,'yyyy-MM-dd')}" />
</div>
</div>
<div class="form-group row">
<label for="inputPoster" class="col-sm-2 col-form-label">Poster</label>
<div class="col-sm-10">
<img th:src="${'../../' + bookView.Book.poster}" alt="Poster"></img>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Author</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:value="${bookView.Author.name}">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Phone</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:value="${bookView.Author.phone}">
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10">
<input type="text" class="form-control" th:value="${bookView.Author.email}">
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<a href="/book/all" class="btn btn-outline-dark" role="button" aria-pressed="true">Back</a>
</div>
</div> </div>
</body>
</html>

5.运行程序

浏览器中输入http://localhost:8080/book/all,显示如下,

点击 Add Book按钮,跳转到add.html页面,

Spring Boot进阶系列三的更多相关文章

  1. Spring Boot进阶系列一

    笔者最近在总结一个 Spring Boot实战系列,以方便将来查找和公司内部培训用途. 1.Springboot从哪里来 SpringBoot是由Pivotal团队在2013年开始研发.2014年4月 ...

  2. Spring Boot进阶系列二

    上一篇文章,主要分析了怎么建立一个Restful web service,系列二主要创建一个H5静态页面使用ajax请求数据,功能主要有添加一本书,请求所有书并且按照Id降序排列,以及查看,删除一本书 ...

  3. Spring Boot进阶系列四

    这边文章主要实战如何使用Mybatis以及整合Redis缓存,数据第一次读取从数据库,后续的访问则从缓存中读取数据. 1.0 Mybatis MyBatis 是支持定制化 SQL.存储过程以及高级映射 ...

  4. 【转】Spring Boot干货系列:(三)启动原理解析

    前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开Sprin ...

  5. Spring Boot干货系列:(三)启动原理解析

    Spring Boot干货系列:(三)启动原理解析 2017-03-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说 ...

  6. Spring Boot干货系列:(七)默认日志框架配置

    Spring Boot干货系列:(七)默认日志框架配置 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候, ...

  7. Spring Boot干货系列:(五)开发Web应用JSP篇

    Spring Boot干货系列:(五)开发Web应用JSP篇 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 上一篇介绍了Spring Boot中使用Thymeleaf模板引擎,今天 ...

  8. Spring Boot干货系列:(四)Thymeleaf篇

    Spring Boot干货系列:(四)Thymeleaf篇 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 Web开发是我们平时开发中至关重要的,这里就来介绍一下Spring Boo ...

  9. Spring Boot 项目学习 (三) Spring Boot + Redis 搭建

    0 引言 本文主要介绍 Spring Boot 中 Redis 的配置和基本使用. 1 配置 Redis 1. 修改pom.xml,添加Redis依赖 <!-- Spring Boot Redi ...

随机推荐

  1. WPF 精修篇 属性动画

    原文:WPF 精修篇 属性动画 属性动画 是通过 Storyboard 来改变属性值 <Rectangle x:Name="rect" Width="200&quo ...

  2. json时间格式化

    //格式化日期字符串 String.prototype.jsonDateFormat = function (format) { var date, timestamp, dtObj timestam ...

  3. 【翻译】tus----一个可续传文件上传的开放协议

    tus tus是一个可续穿文件上传协议,它以Http协议为载体,统一了一个文件断点续传的标准. 这篇文章翻译自https://tus.io/ 目前该协议版本信息如下: Version: 1.0.0 ( ...

  4. SOTA激活函数学习

    除了之前较为流行的RELU激活函数,最近又新出了几个效果较好的激活函数 一.BERT激活函数 - GELU(gaussian error linear units)高斯误差线性单元 数学公式如下: X ...

  5. 2019 淘友天下java面试笔试题 (含面试题解析)

      本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.淘友天下等公司offer,岗位是Java后端开发,因为发展原因最终选择去了淘友天下,入职一年时间了,也成为了面 ...

  6. Spring Boot2(八):性感banner,在线发牌

    本文在个人技术博客[鸟不拉屎]同步发布,详情可猛戳 亦可扫描文章末尾二维码关注个人公众号[鸟不拉屎] emmm,没有啥前言 玩过SpringBoot的都知道,SpringBoot启动的时候,默认会在控 ...

  7. maven 学习---Maven安装配置

    想要安装 Apache Maven 在Windows 系统上, 只需要下载 Maven 的 zip 文件,并将其解压到你想安装的目录,并配置 Windows 环境变量. 所需工具 : JDK 1.8 ...

  8. React Navigation 导航栏样式调整+底部角标消息提示

    五一佳节匆匆而过,有人选择在外面看人山人海,有人选择宅在家中度过五一,也有人依然坚守在第一线,致敬! 这是坚持学习react-native的第二篇文章,可能会迟到,但是绝不会缺席,这篇要涉及到的是re ...

  9. MyCat教程三:安装及配置介绍

    一.安装MyCat 1.安装准备环境 1.1 安装JDK   因为MyCat是java开发的,所以需要java虚拟机环境,在Linux节点中安装JDK是必须的. 1.2 放开相关端口   在主从节点上 ...

  10. Django框架(六)--模板层:变量、过滤器、标签、自定义标签和过滤器

    将页面的设计和Python的代码分离开会更干净简洁更容易维护. 我们可以使用 Django的 模板系统 (Template System)来实现这种模式 # django模板修改的视图函数 def c ...