本节呢给大家介绍一个新鲜 “玩意儿” 叫做Thymeleaf,Thymeleaf翻译过来就是 “百里香叶” 的意思
我发现这些大佬儿特别喜欢用叶子作为标识啊,不过我个人还是挺喜欢这绿叶子 (不是绿帽子!)。说了半天这东西是干啥的呢?客官们接着往下看~
简单来说,Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP,实现网页的 “动态化” ,相较与其他的几个模板引擎,它有如下三个吸引人的特点:
1)Thymeleaf在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
2)Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、
OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。3)Thymeleaf 提供spring标准方言和一个与 SpringMVC完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
总而言之在今后的开发中我们也不会再使用jsp,转而使用Thymeleaf模板替代它,毕竟它是为Spring “量身定做” 的。
1,简单表达式
1、变量的表达式:${…}
2、选择变量表达式:*{…}
3、信息表达式:#{…}
4、链接URL表达式:@{…}
2,字面值 th:text
1、文本文字:‘one text’, ‘Another one!’,…
2、文字数量:0, 34, 3.0, 12.3,…
3、布尔型常量:true, false
4、空的文字:null
5、文字标记:one, sometext, main,…
3,文本处理
1、字符串并置:+
2、文字替换:|The name is ${name}|
4,表达式基本对象
1、#ctx:上下文对象
2、#vars:上下文变量
3、#locale:上下文语言环境
4、#httpServletRequest:(只有在Web上下文)HttpServletRequest对象
5、#httpSession:(只有在Web上下文)HttpSession对象。
5,实用工具对象
1、#dates: java.util的实用方法。对象:日期格式、组件提取等.
2、#calendars:类似于#日期,但对于java.util。日历对象
3、#numbers:格式化数字对象的实用方法。
4、#strings:字符串对象的实用方法:包含startsWith,将/附加等。
5、#objects:实用方法的对象。
6、#bools:布尔评价的实用方法。
7、#arrays:数组的实用方法。
8、#lists:list集合。
9、#sets:set集合。
10、#maps:map集合。
11、#aggregates:实用程序方法用于创建聚集在数组或集合.
12、#messages:实用程序方法获取外部信息内部变量表达式,以同样的方式,因为它们将获得使用# {…}语法
13、#ids:实用程序方法来处理可能重复的id属性(例如,由于迭代)。
之前我们的jsp页面都是存放在webapps中的/WEB-INF/view中,避免外部请求直接通过.jsp的方式访问到这个页面,那么Thymeleaf页面也应该有自己的储存的位置对吧?为了找到答案,我们来看看它的源码,看看能否找到些线索。咱们先找到Thymeleaf模板在SpringBoot中存放的位子,瞅瞅它里面的结构。
发现Thymeleaf包中有三个类,我们选择打开第一个自动配置的类来看看
根据之前的经验,我们可以初步的分析出ThymeleafAutoConfiguration是依赖TemplateMode.class, SpringTemplateEngine.class
这两个类的,另外@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
这个注解之前没有讲过,我来提一下,它的意思就是当前类需要等待被{ }包裹的类加载完之后才能被加载。
那好奇心又勾起了我,到底是什么类这么牛,这么有面子还需要先加载?
说时迟那时快,我点开WebMvcAutoConfiguration,发现这不正是我们之前分析SpringBoot不需要任何配置就能启动的 “灵魂之源” WebMvc自动装配器么?好吧,打扰了,确实Thymeleaf必须要等配置齐全了才能运行…
好,不能再跑偏了,别忘了我们还身负重任!仔细想想,要找到存放你的 “窝” ,那肯定是放在配置文件中写死了的,找找看有没有啥和配置文件搭边的。按照刚才的推想,找到一个properties文件?
private final ThymeleafProperties properties;
点开看看里面有没有我们想要的东西。
果然!找到了Thymeleaf的老窝,根据上面的信息不难推断,存放Thymeleaf的地方就是classpath下的templates文件夹,并且他还默认的给我们后面加上了.html
后缀,这和我们之前的视图解析器的用法是不是很类似?
通过这种方式,我们想要再控制器中实现页面的跳转,直接使用 return index(html页面的名字)
的方式就行啦~
当然有的朋友想说,我已经习惯了之前存放jsp页面的位置,我念旧,不想变动,咋办?
施主既然有要求,贫道自然会帮你解决… 难不倒我的。既然我都知道了Thymeleaf内部是啥结构,我就可以改变它!解决以上问题的办法就是创建一个application.yml,并完成下方的配置就可以了。
好啦,今日的开胃菜已经够多了,该上 “主食” 了,不然撑不下去的。
在创建项目之前呢,大家先安装一下Thymeleaf代码提示功能,直接在在Eclipse中安装Thymeleaf插件即可。
插件的地址为:thymeleaf插件下载地址
安装方式参考博文(PS:太懒了…不想写…) https://blog.csdn.net/king_kgh/article/details/76084398
插件下载完成之后,咱们先创建一个SpringBoot项目,我会带着大家看看如何使用Thymeleaf,比看概念强一万倍!
注意了,在Avalilable这里搜索thyme,勾选之后,SpringBoot会帮我们导入thyme的包
其实SpringBoot中默认的配置了thymeleaf,只不过被
包裹起来了,因此当我们需要的时候,直接配置,不用填写版本号。
第一步:引入layui
第二步:创建common.properties
在src/main/resources目录下创建common.properties作为数据源
第三步:创建showStudent.html
引入标签之后,会出现如下的自动提示
这需要注意css和javascript导入资源文件标签是不一样的,css使用的是th:href
,javascript使用的是th:src
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="#{common.title}">Insert title heretitle>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}" media="all">
<script th:src="@{/layui/layui.js}" charset="utf-8">script>
head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend th:text="#{student.title}">legend>
fieldset>
<div style="padding: 20px; background-color: #F2F2F2;">
<div class="layui-row layui-col-space15">
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-body" th:text="#{student.id}">学生编号div>
<div class="layui-card-body" th:text="#{student.name}">学生姓名div>
<div class="layui-card-body" th:text="#{student.sex}">学生性别div>
<div class="layui-card-body" th:text="#{student.address}">学生地址div>
<div class="layui-card-body" th:text="#{student.phone}">学生电话div>
<div class="layui-card-body" th:text="#{student.age}">学生年龄div>
<div class="layui-card-body" th:text="#{student.birth}">学生日期div>
div>
div>
div>
div>
body>
html>
第四步:创建IndexController
package com.marco.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("index")
public class IndexController {
/**
* 跳转到template文件夹下的index.html
* @return
*/
@RequestMapping("goToIndex")
public String goToIndex() {
return "index";
}
/**
* 跳转到template文件夹下的showStudent.html
* @return
*/
@RequestMapping("showStudent")
public String showStudent() {
return "showStudent";
}
}
第五步:测试
弄了半天,数据都 “乱码” 了?是不是编码没设置好啊?
其实数据 “乱码” 原因本质上是值没有取到,因为common.properties没有被加载。那怎么去加载我们的配置文件呢?解决上面的问题,需要创建咱们配置一下,我这里就命名为I18NConfig。
I18N (其来源是英文单词internationalization的首末字符i和n,18为中间的字符数)是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。
在全球化的时代,国际化尤为重要,因为产品的潜在用户可能来自世界的各个角落。通常与i18n相关的还有L10n(“本地化”的简称)。
这里需要提到的一点,关于方法messageSource.setBasename("common")
package com.marco.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
@Configuration
public class I18NConfig {
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setFallbackToSystemLocale(false);
messageSource.setBasename("common");//需要被读取的文件的名字,.properties缺省
messageSource.setDefaultEncoding("UTF-8");//设置默认的字符编码
messageSource.setCacheSeconds(2);//以秒为单位
return messageSource;
}
}
添加了I18NConfig之后我们再来看看效果吧
数据获取到了,没有问题!不过还没结束,我们接着创建另外一个配置文件common_zh_CN.properties
,并且在里边用中文将我们刚才的内容翻译并替换掉
第六步:创建common_zh_CN.properties
什么鬼?我打的是中文啊… 这是因为,在SpringBoot中创建的properties文件,只要输入中文都会自动的转成unicode编码格式。如果想正常的使用中文,需要定义为yml文件。
第七步:测试
欸?我们发现在两个properties文件同时存在的情况下,SpringBoot 默认会先解析common_zh_CN.properties文件,这是为什么呢?
因为此时的操作系统的语言环境为中文 所以读取的是common_zh_CN.properties里面的内容,如果是英文环境,则默认会取读取的是common_en_US.properties的内容,原因是SpringBoot默认支持国际化信息,也就是当我们访问一个网站切换中英界面时,会推送出不同的页面。
SpringBoot能支持国际化信息,本质上是内置了区域解析器,我们点开defaultLocale就可以发现里面定义了很多国家的语言,因此当他读取配置文件的时候也会默认先去读取这些包含国际化信息的properties文件
上面我们使用的是固定的文本格式获取数据,这种方式使用的并不多,接着我们来看看怎么获取Servlet作用域中的值吧~
第一步:创建Student
第二步:修改IndexController
在之前的IndexController中添加下面的方法,我们一共添加6条数据,并使用Model容器装配数据
/**
* 跳转到template文件夹下的showStudent.html
* @return
*/
@RequestMapping("showAllStudent")
public String showAllStudent(Model model) {
List<Student> students = new ArrayList<>();
for (int i = 1; i <= 6; i++) {
Student student = new Student(i, "marco" + i, 18 + i,
i%2 == 0 ? "男" : "女", "13595623654" + i, "武汉" + i, new Date());
students.add(student);
}
model.addAttribute("students", students);
return "showAllStudent";
}
第三步:创建showAllStudent.html
之前我们在jsp中可以通过jstl的foreach标签来循环获取多条数据,那么在Thymeleaf中当然也会有类似的方法,我们先来找找看,发现有个和foreach还比较类似的标签,那决定就是你了!
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="#{common.title}">Insert title heretitle>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}" media="all">
<script th:src="@{/layui/layui.js}" charset="utf-8">script>
head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
<legend th:text="#{student.title}">legend>
fieldset>
<div style="padding: 20px; background-color: #F2F2F2;">
<div class="layui-row layui-col-space15">
<div class="layui-col-md4" th:each="student:${students}">
<div class="layui-card">
<div class="layui-card-body" th:text="${student.id}">学生编号div>
<div class="layui-card-body" th:text="${student.name}">学生姓名div>
<div class="layui-card-body" th:text="${student.sex}">学生性别div>
<div class="layui-card-body" th:text="${student.address}">学生地址div>
<div class="layui-card-body" th:text="${student.phone}">学生电话div>
<div class="layui-card-body" th:text="${student.age}">学生年龄div>
<div class="layui-card-body" th:text="${#dates.format(student.birth,'yyyy:MM:dd HH:mm:ss')}">学生日期div>
div>
div>
div>
div>
body>
html>
不知道大家发现没有,当我们获取文本的数据的时候,使用得是#{common.title}
这种方式,使用链接得时候使用的是th:src="@{/layui/layui.js}"
这种方式,获取作用域对象得属性值时又换了种方式th:text="${student.address}"
,是不是觉得很麻烦?但是仔细想想,这里边也有规律可以寻,比如说我们之前使用xxxMapper.xml的时候,想获取值就是通过#{ }
,而xml刚好是文本,获取的是文本值,之前在jsp页面种也是通过${ }
的方式获取值,因此,这个也比较好记啦,剩下的链接@{ }
你可以把它想象成email的格式,email的信息本身也相当于链接(邮箱的链接),这样去记,就不会混淆啦~
大家回忆一下,之前呢我们在js种怎么取到request作用域种的值的。是不是下面这种方式?
那么我们来测试下看看在Thymeleaf中,这样是否行得通,这样我们修改下后台的IndexController,因为之前后台传过来的是一个List,我们这样取值肯定会出事的,所以我们来添加一个showOneStudent()的方法。
/**
* 跳转到template文件夹下的showOneStudent.html
* @return
*/
@RequestMapping("showOneStudent")
public String showOneStudent(Model model) {
Student student = new Student(1, "marco", 18, "男", "13652145236", "武汉", new Date());
model.addAttribute("student", student);
return "showOneStudent";
}
前台把th:each
中的内容去掉就好啦,我就不重复上代码了。创建好之后我们来测试一下。
咦额… 好像报错了,那证明我们的想法是错的,jsp中的那一套在Thymeleaf里还是不顶用啊,那该怎么做呢?
这才是正确的打开方式… 这个中括号感觉很熟悉对不?
我们之前在layui中的表格的cols标签有使用过类似的方式
Mapper.xml中的标签也是有两个中阔号包裹,所以说
[[]]
这种方式并不是Thymeleaf独创的,使用的时候一定要避免冲突,特别是和layui结合使用的时候,稍微提一下,如果在使用Thymeleaf时也是用到了layui,那么layui的中的cols标签的[[]]
之间要加上空格,例如[ [] ]
。
我们这里再稍微拓展一下如何访问带参数的消息
我们在之前的common.properties文件中加上common.welcome=welcome {0} to China {1}
接着咱们在页面上添加标签来看看效果
好像没啥特别的啊?不急,我们接着修改刚加进去的页面标签
<legend th:text="#{common.welcome('marco','~')}">legend>
Thymeleaf为我们提供了一种特殊的url传值方式,称之为链接传值,大家可以看到下面的第一种方式是我们的普通传值方式,第二种就是Thymeleaf的链接传值方式了(利用 th:href
标签)。
老样子,还是来测试一下是否可以获取到()中的值,点击登录按钮
页面报错了咱不管,不过值是顺利的获取到了!
ThymeleafObjects指的就是作用域中的值,学习过Servlet的朋友肯定对request、session、application等名词不会陌生,之前学习jsp的时候,我们就是从这几个作用域中获取值,从而做出一个个动态的页面,jsp能做出动态的页面本质是因为jsp就是一个Servlet,但是本节我们学习的Thymeleaf其实就是一个html页面,只不过是一个比较 “特殊” 的html页面,特殊就特殊在使用它也能够做出动态页面的效果,jsp能做的事它也能做到,比如说从Servlet作用域中获取值!
我们还是拿案例说事儿,咱们在IndexController中事先准备一个方法,存点儿东西到上面提到的三个作用域里
/**
* 跳转到template文件夹下的showObject.html
* @return
*/
@RequestMapping("showObject")
public String showObject(Model model, HttpServletRequest request) {
request.setAttribute("msg", "i'm request");
request.getSession().setAttribute("msg", "i'm session");
request.getServletContext().setAttribute("msg", "i'm context");
return "showObject";
}
然后准备一个小巧的页面
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="#{common.title}">Insert title heretitle>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}" media="all">
<script th:src="@{/layui/layui.js}" charset="utf-8">script>
head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
fieldset>
<div style="padding: 20px; background-color: #F2F2F2;">
<div class="layui-row layui-col-space15">
<div class="layui-col-md4">
<div class="layui-card">
Request:<div class="layui-card-body" th:text="${msg}">div>
Session:<div class="layui-card-body" th:text="${session.msg}">div>
Context:<div class="layui-card-body" th:text="${application.msg}">div>
div>
div>
div>
div>
body>
html>
是不是很简单?其实和jsp获取值的方式是差不多的,比如说之前jsp页面从session中取值是使用${sessionScope.attribute}
,而在Thymeleaf中使用的是${session.attribute}
,写起来更简单了!
我们还是来看看页面上的效果吧~
到此为止,咱们的Thymeleaf就介绍完啦,其实使用方式和jsp没有特别大的区别,只不过Thymeleaf的功能似乎划分的更细,更强大,因此合理的运用这 “百里香叶” 是非常有必要的,多练习吧~