SpringBoot整合Thymeleaf构建WEB应用
云南汉王科技Java8 SpringBoot
框架开发培训
Thymeleaf 是一种模板语言。那模板语言或模板引擎是什么?常见的模板语言都包含以下几个概念:数据(Data)、模板(Template)、模板引擎(Template Engine)和结果文档(Result Documents)。
Spring boot 支持多种模板语言(Thymeleaf 、Freemarker、Mustache、Groovy Templates)Thymeleaf 跟大部分的模板语言类似,上手容易,使用简单。
一、增加Spring boot的maven 依赖
在原有基础的pom结构中追加Thymeleaf的依赖
<dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency >
二、编写控制层代码
配置SpringBoot
,创建文件application.yml
server: port: 8080 session-timeout: 60 *60 tomcat: url-encoding: utf8 compression: true spring: thymeleaf: cache: false datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://120.79.90.37:5432/learning username: learning password: ******** db-name: learning db-schema: swagger2 mybatis-plus: mapper-locations: classpath:/com/hanwang/restful/mapper/xml/*Mapper.xml typeAliasesPackage: com.hanwang.restful.entity typeEnumsPackage: com.baomidou.springboot.db.entity.enums global-config: db-config: id-type: uuid field-strategy: not_empty column-underline: true logic-delete-value: Y logic-not-delete-value: N db-type: pgsql refresh: true sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector configuration: map-underscore-to-camel-case: true cache-enabled: false generator: module-name: restful01 author: gadfly file-override: true
实体类
import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableName;import com.baomidou.mybatisplus.extension.activerecord.Model;import com.baomidou.mybatisplus.annotation.TableId;import java.time.LocalDateTime;import com.baomidou.mybatisplus.annotation.TableField;import java.io.Serializable;import lombok.Data;import lombok.EqualsAndHashCode;import lombok.experimental.Accessors;import pub.gadfly.sdk.Handler.ByteArrayToByteaPG;@Data @EqualsAndHashCode (callSuper = true )@Accessors (chain = true )@TableName (value = "user" , schema = "swagger2" )public class User extends Model <User > { private static final long serialVersionUID=1L ; @TableId (value = "id" , type = IdType.AUTO) private Integer id; @TableField ("realname" ) private String realname; @TableField ("mobile" ) private String mobile; @TableField ("email" ) private String email; @TableField ("public_key" ) private String publicKey; @TableField ("private_key" ) private String privateKey; @TableField ("insert_time" ) private LocalDateTime insertTime; @TableField ("modify_time" ) private LocalDateTime modifyTime; @TableField (value = "head_pic" , typeHandler = ByteArrayToByteaPG.class ) private byte[] headPic; public static final String ID ="id" ; public static final String REALNAME ="realname" ; public static final String MOBILE ="mobile" ; public static final String EMAIL ="email" ; public static final String PUBLIC_KEY ="public_key" ; public static final String PRIVATE_KEY ="private_key" ; public static final String INSERT_TIME ="insert_time" ; public static final String MODIFY_TIME ="modify_time" ; public static final String HEAD_PIC ="head_pic" ; @Override protected Serializable pkVal () { return this .id; } }
模型类
import lombok.Data;import pub.gadfly.sdk.Utils.StreamUtil;@Data public class UserPO { private Integer id; private String realname; private String email; private String mobile; private String headPic; public void setHeadPic (byte [] headPic) { try { this .headPic = StreamUtil.bytes2Base64Pic(headPic); } catch (Exception e) { e.printStackTrace(); } } }
数据库映射接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.hanwang.web.entity.User;import org.apache.ibatis.annotations.Mapper;import org.springframework.stereotype.Repository;@Mapper @Repository public interface UserMapper extends BaseMapper <User > {}
数据库映射实体类
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace ="com.hanwang.restful01.mapper.UserMapper" > <cache type ="org.mybatis.caches.ehcache.LoggingEhcache" /> <resultMap id ="BaseResultMap" type ="com.hanwang.web.entity.User" > <id column ="id" property ="id" /> <result column ="realname" property ="realname" /> <result column ="mobile" property ="mobile" /> <result column ="email" property ="email" /> <result column ="public_key" property ="publicKey" /> <result column ="private_key" property ="privateKey" /> <result column ="insert_time" property ="insertTime" /> <result column ="modify_time" property ="modifyTime" /> <result column ="head_pic" property ="headPic" /> </resultMap > <sql id ="Base_Column_List" > id, realname, mobile, email, public_key, private_key, insert_time, modify_time, head_pic </sql > </mapper >
控制器
import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Map;@Controller @RequestMapping ("/user" )public class UserController { @Autowired private UserMapper userMapper; @RequestMapping (value="" , method= RequestMethod.GET) public String index (Model model) { List<UserPO> userList = new ArrayList<>(); userMapper.selectList( null ).forEach( (user) -> { UserPO userPO = new UserPO(); userPO.setEmail(user.getEmail()); userPO.setRealname(user.getRealname()); userPO.setMobile(user.getMobile()); userPO.setId(user.getId()); if (user.getHeadPic()!=null ){ userPO.setHeadPic(user.getHeadPic()); } userList.add(userPO); } ); model.addAttribute("userList" , userList); return "user/index" ; } @RequestMapping (value = "/addUser" , method = RequestMethod.GET) public String addUser () { return "user/addUser" ; } @RequestMapping (value = "/addUser" , method = RequestMethod.POST) public String addUser ( @RequestParam("realname" ) String realname, @RequestParam ("mobile" ) String mobile, @RequestParam ("email" ) String email ) { System.out.println("realname: " + realname + ", mobile: " + mobile + ", email: " + email); User user = new User(); user.setEmail( email ); user.setRealname( realname ); user.setMobile( mobile ); _register(user); return "redirect:/user" ; } @RequestMapping (value = "/register" , method = RequestMethod.GET) public String register (Model model) { model.addAttribute("user" , new User()); return "user/register" ; } @RequestMapping (value = "/register" , method = RequestMethod.POST) public String register (@ModelAttribute User user, @RequestParam("file" ) MultipartFile file) { try { user.setHeadPic(file.getBytes()); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } System.out.println(user.toString()); _register(user); return "redirect:/user" ; } private void _register (User user) { try { Map<String, Object> genKeyPair = RSA.genKeyPair(); user.setPrivateKey( RSA.getPrivateKey(genKeyPair) ); user.setPublicKey( RSA.getPublicKey(genKeyPair) ); user.insert(); } catch (Exception e) { e.printStackTrace(); } } }
三、编写布局页面
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"> <head> <meta content="text/html;charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE-edge" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <link href="https://bootswatch.com/3/cyborg/bootstrap.min.css" rel="stylesheet" /> <title>Title</title> </head> <body> <div class="container"> <nav class="navbar navbar-default"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <a class="navbar-brand" href="#">个人博客</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">欢迎页</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">博客 <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">博客列表</a></li> <li><a href="#">发布博客</a></li> </ul> </li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">用户 <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a th:href="@{/user}">用户列表</a></li> <li><a th:href="@{/user/addUser}">注册用户(字段提交)</a></li> <li><a th:href="@{/user/register}">注册用户(对象提交)</a></li> </ul> </li> </ul> </div> </nav> <div class="panel panel-default"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body" th:include="::content"> Panel content </div> </div> </div> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js" type="text/javascript"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <script th:inline="javascript"> var single = [[${singlePerson}]]; console.log(single.name + "/" + single.age) function getName(name) { alert(name); } </script> </body> </html>
四、编写列表页面
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org" th:replace="layout"> <div th:fragment="content"> <h3>用户列表</h3> <ul class="list-group" th:each="user: ${userList}"> <li class="list-group-item row"> <div class="col-md-1 thumbnail"> <img th:src="${user.headPic}" alt="" class="img-circle" style="width: 30px"> </div> <div class="col-md-4"> <span>真实姓名:</span> <span th:text="${user.realname}"></span> </div> <div class="col-md-4"> <span>联系电话:</span> <span th:text="${user.mobile}"></span> </div> <div class="col-md-3"> <span>电子邮件:</span> <span th:text="${user.email}"></span> </div> </li> </ul> </div> </html>
五、编写增加页面
对象提交页
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org" th:replace="layout"> <div th:fragment="content"> <h3>用户注册</h3> <form th:action="@{/user/register}" method="post" th:object="${user}" enctype="multipart/form-data"> <div class="form-group"> <label for="exampleInputRealname">真实姓名</label> <input type="text" th:field="*{realname}" class="form-control" id="exampleInputRealname" placeholder="真实姓名"> </div> <div class="form-group"> <label for="exampleInputMobile">联系电话</label> <input type="text" th:field="*{mobile}" class="form-control" id="exampleInputMobile" placeholder="联系电话"> </div> <div class="form-group"> <label for="exampleInputEmail">电子邮件</label> <input type="text" th:field="*{email}" class="form-control" id="exampleInputEmail" placeholder="电子邮件"> </div> <div class="form-group"> <label for="exampleInputHeadPic">上传头像</label> <input type="file" name="file" class="form-control" id="exampleInputHeadPic"> </div> <button type="submit" class="btn btn-default">提交</button> </form> </div> </html>
字段提交页
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org" th:replace="layout"> <div th:fragment="content"> <h3>用户注册</h3> <form th:action="@{/user/addUser}" method="post"> <div class="form-group"> <label for="exampleInputRealname">真实姓名</label> <input type="text" name="realname" class="form-control" id="exampleInputRealname" placeholder="真实姓名"> </div> <div class="form-group"> <label for="exampleInputMobile">联系电话</label> <input type="text" name="mobile" class="form-control" id="exampleInputMobile" placeholder="联系电话"> </div> <div class="form-group"> <label for="exampleInputEmail">电子邮件</label> <input type="text" name="email" class="form-control" id="exampleInputEmail" placeholder="电子邮件"> </div> <button type="submit" class="btn btn-default">提交</button> </form> </div> </html>
六、Thymeleaf
的使用与语法
坑:创建Thymeleaf
模板时注意,把html
的名称空间,改成:xmlns:th="http://www.thymeleaf.org"
<!DOCTYPE html> <html lang="zh-CN" xmlns:th="http://www.thymeleaf.org"> ...... </html>
Thymeleaf
的默认参数配置
如有需要修改默认配置的时候,只需复制下面要修改的属性到application.properties中,并修改成需要的值,如修改模板文件的扩展名,修改默认的模板路径等。
spring.thymeleaf: cache: true check-template: true check-template-location: true enabled: true encoding: UTF-8 spring.thymeleaf.excluded-view-names: mode: HTML5 prefix: classpath:/templates/ suffix: .html template-resolver-order: spring.thymeleaf.view-names:
Thymeleaf
语法
基本表达式
变量的表达式:${...}
用于访问 容器上下文环境 中的变量
<span th:text="${information}">
选择变量表达式:*{...}
选择表达式计算的是 选定的对象 (th:object对象属性绑定的对象)
<div th:object="${session. user}" > Name: <span th: text=" *{firstName}" >Sebastian. Surname: <span th: text=" *{lastName}" >Pepper. Nationality: <span th: text=" *{nationality}" >Saturn. </div>
信息表达式:#{...}
一般用于 显示页面静态文本。将可能需要根据需求而整体变动的静态文本放在properties文件中,方便维护。通常与th:text属性一起使用。
<!- 新建/WEB-INF/templates/home.html,段落: -> <p th: text=" #{home. welcome}" >This text will not be show! <!- 新建/WEB-INF/templates/home.properties: -> home.welcome=this messages is from home.properties!
链接URL表达式:@{...}
用于链接。可以链接目录下的静态资源,也可以链接到后端请求处理接口
<link th:src="@{/resources/css/bootstrap.min.css}" /> <li><a th:href="@{/forIndex}">首页</a></li>
工具对象表达式:#maps
常用于 日期、集合、数组对象的访问 。这些工具对象就像是java对象,可以访问对应java对象的方法来进行各种操作。
<div th:if="${#maps.size(stu)!=0}"> ...do something... </div>
#dates
: java.util的实用方法。对象:日期格式、组件提取等.
#calendars
: 类似于#日期,但对于java.util。日历对象
#numbers
: 格式化数字对象的实用方法。
#strings
:字符串对象的实用方法:包含startsWith,将/附加等。
#objects
: 实用方法的对象。
#bools
: 布尔评价的实用方法。
#arrays
: 数组的实用方法。
#lists
: list集合。
#sets
:set集合。
#maps
: map集合。
#aggregates
: 实用程序方法用于创建聚集在数组或集合.
#messages
: 实用程序方法获取外部信息内部变量表达式,以同样的方式,因为他们将获得使用# {…}语法
#ids
: 实用程序方法来处理可能重复的id属性(例如,由于迭代)。
#httpServletRequest
:用于web应用中获取request请求的参数
#session
:用于web应用中获取session的参数
常用属性
Thymeleaf语法的使用,都是通过在html页面的标签中添加th:xx关键字来实现模板套用,且其属性与html页面标签基本类似
th:action
表单提交的地址 <form action="subscribe.html" th:action="@{/subscribe}">
定义后台控制器路径,类似
th:each
属性赋值 <tr th:each="user,userStat:${users}">
对象遍历,功能类似jsp中的<c:forEach>标签。用于遍历展示或者遍历录入。
<form th:action="@{/addStudent}" th:object="${stuReqBean}" method="POST"> <div th:each="stuIter,rowStat:${stuReqBean.students}"> <input type="text" value="" th:field="*{students[__${rowStat.index}__].firstName}"></input> <input type="text" value="" th:field="*{students[__${rowStat.index}__].school}"></input> ... </div> </form>
@RequestMapping (value = "/addStudent" , method = RequestMethod.POST)public String addStudent (@ModelAttribute(value = "stuReqBean" ) { StudentRequestBean stuReqBean,ModelMap model) {...} }
th:field
常用于表单字段绑定 <input type="text" value="" th:field="*{username}"></input>
常用于表单字段绑定。通常与th:object一起使用。 属性绑定、集合绑定。
<form th:action="@{/login}" th:object="${loginBean}">... <input type="text" value="" th:field="*{username}"></input> <input type="text" value="" th:field="*{user[0].username}"></input> </form>
th:href
链接地址 <a th:href="@{/login}" th:unless=${session.user != null}>Login</a> />
定义超链接,类似标签的href 属性。value形式为@{/logout}
th:id
替换id <input th:id="'xxx' + ${collect.id}"/>
div id声明,类似html标签中的id属性。
th:if
判断条件 <a th:if="${userId == collect.userId}" >
条件判断。如果为否则标签不显示
<div th:if="${rowStat.index} == 0">... do something ...</div>
th:include
布局标签,替换内容到引入的文件 <head th:include="layout :: htmlhead" th:with="title='xx'"></head> />
声明定义该属性的div为模板片段,常用与头文件、页尾文件的引入。常与th:include,th:replace一起使用。
<!- 声明模板片段/WEBINF/templates/footer.html : -> <div th: fragment=" copy" > © 2011 The Good Thymes Virtual Grocery </div> <!- 引入模板片段:-> <div th: include=" /templates/footer : : copy" ></div> <div th: replace=" /templates/footer : : copy" ></div> </div>
th:fragment
布局标签,定义代码片段,其它引用 <div th:fragment="alert">
th:object
替换对象 <div th:object="${session.user}">
用于表单数据对象绑定,将表单绑定到后台controller的一个JavaBean参数。常与th:field一起使用进行表单数据绑定。
public class LoginBean implements Serializable {...}@RequestMapping (value = "/login" , method = RequestMethod.POST)public String login (@ModelAttribute(value = "loginBean" ) LoginBean loginBean,ModelMap model) {...}
<form th:action="@{/login}" th:object="${loginBean}">...</form>
th:src
图片类地址引入 
用于外部资源引入,类似于