SpringCloud与Thymeleaf整合demo
最近在学习SpringCloud微服务的整合,这段时间也挺迷茫的,不太确定自己的方向,特别是前端这块。最后综合各方面的因素,决定学习Thymeleaf模版引擎,它也是SpringBoot官方推荐的模版引擎。先来介绍下Thymeleaf:- Thymeleaf是⾯向Web和独⽴环境的现代服务器端Java模板引擎, 能够处理HTML, XML, JavaScript, CSS甚⾄...
·
最近在学习SpringCloud微服务的整合,这段时间也挺迷茫的,不太确定自己的方向,特别是前端这块。最后综合各方面的因素,决定学习Thymeleaf模版引擎,它也是SpringBoot官方推荐的模版引擎。
先来介绍下Thymeleaf:
- Thymeleaf是⾯向Web和独⽴环境的现代服务器端Java模板引擎, 能够处理HTML, XML, JavaScript, CSS甚⾄纯⽂本。
- Thymeleaf旨在提供⼀个优雅的、 ⾼度可维护的创建模板的⽅式。 为了实现这⼀⽬标, Thymeleaf建⽴在⾃然模板的概念上,将其逻辑注⼊到模板⽂件中, 不会影响模板设计原型。 这改善了设计的沟通, 弥合了设计和开发团队之间的差距。
- Thymeleaf从设计之初就遵循Web标准——特别是HTML5标准 , 如果需要, Thymeleaf允许您创建完全符合HTML5验证标准的模板。
Thymeleaf的优势:
A.Thymeleaf 在有网络和无网络的环境下皆可运行,而且完全不需启动WEB应用,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
B.Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
C.Thymeleaf 提供spring标准方言和一个与 SpringMVC完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
总的来说,我个人认为:Thymeleaf最大的优势就是完美的支持H5及前端开发人员可以实现与后台开发人员的无缝交接,这也是实现前后端分离的最主要的技术。
关于Thymeleaf的资料大家自行上网查看,我今天主要来跟大家分享SpringClou与Thymeleaf模版引擎整合的案例(ORM框架采用的是Mybatis)。
首先,搭建Eureka服务器。然后搭建一个服务提供者和服务消费者的微服务,并将这两个微服务注册到Eureka服务器中。具体看如下代码:
//创建User对象,其中grade表示User读过的班级list
public class User {
private Integer uid;
private String userName;
private Integer age;
private List<Grade> grade;
public User(Integer uid, String userName, Integer age, List<Grade> grade) {
this.uid = uid;
this.userName = userName;
this.age = age;
this.grade = grade;
}
public User(){}
public long getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<Grade> getGrade() {
return grade;
}
public void setGrade(List<Grade> grade) {
this.grade = grade;
}
}
//创建Grade对象
public class Grade {
private Integer gid;
private String userName;
private String gradeName;
private String teacherName;
public Grade(Integer gid, String userName,String gradeName, String teacherName) {
this.gid = gid;
this.userName = userName;
this.gradeName = gradeName;
this.teacherName = teacherName;
}
public Grade(){}
public long getGid() {
return gid;
}
public void setGid(Integer gid) {
this.gid = gid;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getGradeName() {
return gradeName;
}
public void setGradeName(String gradeName) {
this.gradeName = gradeName;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
}
数据库设计如下:
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`uid` int(9) NOT NULL AUTO_INCREMENT,
`userName` varchar(8) NOT NULL,
`age` tinyint(4) NOT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `t_grade`;
CREATE TABLE `t_grade` (
`gid` int(9) NOT NULL AUTO_INCREMENT,
`userName` varchar(8) NOT NULL,
`gradeName` varchar(12) NOT NULL,
`teacherName` varchar(8) NOT NULL,
PRIMARY KEY (`gid`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
Mapper映射文件:
<mapper namespace="com.xue.test.dao.UserMapper">
<resultMap id="userMap" type="com.xue.test.domain.User">
<id property="uid" column="uid"/>
<result property="userName" column="userName"/>
<result property="age" column="age"/>
<collection property="grade" ofType="com.xue.test.domain.Grade">
<id property="gid" column="gid"/>
<result property="userName" column="userName"/>
<result property="gradeName" column="gradeName"/>
<result property="teacherName" column="teacherName"/>
</collection>
<!-- property表示集合类型属性名称,ofType表示集合中的对象是什么类型 -->
</resultMap>
<select id="findUserByName" parameterType="java.lang.String" resultMap="userMap">
select t1.*,t2.* from t_user t1 left join t_grade t2 on t1.userName = t2.userName
<where>
and t1.userName = #{userName}
</where>
</select>
<insert id="saveUser" parameterType="com.xue.test.domain.User">
insert into t_user(userName,age) values(#{userName},#{age});
insert into t_grade(userName,gradeName,teacherName)
values
<foreach collection="grade" item="item" index="index" separator=",">
(#{item.userName},#{item.gradeName},#{item.teacherName})
</foreach>
<!-- 不要盲目使用以下语句:
1. open="(" :表示以'('开始
2. separator="," : 表示以','分隔
3. close=")" : 表示以')'结束
以上三个属性是可选的
<foreach collection="list" item="id" index="index" open="(" separator="," close=")">
#{id}
</foreach>
-->
</insert>
先说说我在用Mybatis映射对象时遇到的问题:
1.当我把<select/>标签的<where/>标签添加<if/>判断的时候,即如下
<where>
<if test="userName!=null and userName!=''">
and t1.userName = #{userName}
</if>
</where>
Mybatis报下异常:
org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userName' in 'class java.lang.String'
只需要用删除:<if test="userName!=null and userName!=''"></if>,就不会报错了。
2.上面的User和Grade都有两个构造器,一个是有参构造器,一个是无参构造器。下面来说说它们的作用:
A.有参构造器的作用:必须要有默认的构造器,否则,SpringCloud Feign根据json字符串转换成User时会抛出异常.
B.无参构造器的作用:预防Mybatis映射报异常.
当我把无参构造器去掉时,以User为例:
去掉 public User(){}
Mybatis报如下异常:
nested exception is org.apache.ibatis.executor.ExecutorException: No constructor found in com.xue.test.domain.User matching [java.lang.Integer, java.lang.String, java.lang.Integer, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String]] with root cause
所有以后创建domain对象的时候,记得要同时创建有参构造器及无参构造器
导入Thymeleaf的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
application.yml中添加如下配置:
#thymelea模板配置
spring:
thymeleaf:
cache: false
mode: HTML5
encoding: UTF-8
servlet:
content-type: text/html
prefix: classpath:/templates
suffix: .html
resources:
chain:
strategy:
content:
enabled: true
paths: /**
我是使用Feign进行调用的,整合了Hytrix,代码如下:
//定义FeignClient接口
@FeignClient(name="server-provide",fallback = UserHystrixClient.class)
public interface UserFeignClient {
@GetMapping("/user/{userName}")
User findUserByName(@PathVariable("userName") String userName);
}
//定义回退方法的类
@Component
public class UserHystrixClient implements UserFeignClient {
@Override
public User findUserByName(String userName) {
User user = new User();
user.setUid(0);
user.setUserName("Hystrix");
user.setAge(18);
ArrayList<Grade> gradeList = new ArrayList<>();
Grade grade = new Grade();
grade.setGid(0);
grade.setUserName("Hystrix");
grade.setGradeName("Hystrix");
grade.setTeacherName("Hystrix");
gradeList.add(grade);
user.setGrade(gradeList);
return user;
}
}
UserController的调用代码:
@RestController
public class UserController {
@Resource
private UserFeignClient userFeignClient;
@GetMapping("/user/{userName}")
public ModelAndView showUserDetails(@PathVariable String userName){
User user = userFeignClient.findUserByName(userName);
List<Grade> gradeList = user.getGrade();
for(Grade grade:gradeList){
grade.getTeacherName();
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("user",user);
//记得一定要将"/"加上
modelAndView.setViewName("/test");
return modelAndView;
}
}
目录结构:
使用Thymeleaf后test.html的写法:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Test</title>
<link href="../static/css/basic.css" rel="stylesheet" type="text/css" th:href="@{/css/basic.css}"/>
</head>
<body>
<table class="basictable">
<thead>
<tr>
<th th:text="${user.uid}">uId</th>
<th th:text="${user.userName}">userName</th>
<th th:text="${user.age}">age</th>
</tr>
</thead>
<tbody>
<tr th:each="grade:${user.grade}">
<td th:text="${grade.gid}">gid</td>
<td th:text="${grade.gradeName}">gradeName</td>
<td th:text="${grade.teacherName}">teacherName</td>
</tr>
</tbody>
</table>
</body>
</html>
未加载数据时:
加载数据后:
SpringClou+Thymeleaf整合步骤就演示完了。当前只做了数据的展示,后续我会更新Thymeleaf表单数据的提交及Thymeleaf的ajax提交的做法。
关于我
可以扫描关注下面的公众号(公众号:猿类进化论)
更多推荐
已为社区贡献1条内容
所有评论(0)