最近在学习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提交的做法。

关于我


    可以扫描关注下面的公众号(公众号:猿类进化论)
在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐