ABtest原理及用法总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABtest原理及用法总结相关的知识,希望对你有一定的参考价值。
参考技术A ABtest原理是什么,其实ABtest的基本原理是当我们在A、B两个方案之间犹豫时,直接把两个方案测试一把,看看哪个效果好,把测试结果作为参考依据,但是ABtest的难度主要在开发上:开发新版本、进行测试、测试数据回传保存等等。一、ABtest的基本原理
简单来说,ABtest就是当我们在A、B两个方案之间犹豫不决的时候,直接把两个方案测试一把,看看哪个效果好,把测试结果作为参考依据。
因为是抽样测试,所以需要一些统计学方法,估计是否测试能代表整体情况,因此要引用统计学方法。
二、ABtest如何做
ABtest本质上是一个: 两总体 假设检验问题,要检验A、B两个版本是一样,还是有所区别。和之前介绍的单总体假设检验一样,两总体假设检验,也分步骤:
第一步:明确要检验的A、B两个对象
第二步:明确要检验的指标,是平均值,还是比例
第三步:根据检验目的,给出原假设/备选假设
第四步:根据要检验的指标,选择检验统计量(是不是头都看大了,其实没关系,大部分统计软件,包括excel,都有提供两样本均值的t检验,直接用即可)。
第五步:给定显著性水平,计算统计量,得出结果:支持原假设还是推翻原假设
这里的原理和假设检验是一模一样的,小伙伴们可简单理解成:检验的是两个方案的均值/比例,是否等于0。
是不是头又看大了,没关系,这些计算结果在统计软件中都可以直接给出。小伙伴们只要记得: P值小于0.05推翻原假设 ,就OK啦!
三、ABtest小例子
来看个具体例子:某电商平台,想提升用户客单价,运营部门做了两套方案:A、B激励方案,想小规模投放优惠给用户,测试下效果。已选出两组各12名用户,测试用户客单价如下图所示。此时可以用excel自带的两总体t检验功能来做:
第一步,先准备好A、B两个方案的数据
第二步:明确,要检验的是平均值(检验两个方案的平均客单价是否一致);
第三步:给出原假设。这里可以简单处理,提假设;
原假设:方案A客单价均值=方案B客单价均值;
备选假设:两者不相等。
这样怼翻了原假设,就说明方案A和方案B有差异啦(记得把要怼翻的放原假设哦)
第四步:代入t检验公式计算;
第五步:解读结论。
既然方案A与方案B不同,A的均值又高于B,那么就认为A更好啦,搞掂!
这样就做完啦!
四、ABtest的注意事项
注意1:测试目标要清晰。 在实际工作中,有些产品经理/运营的口头禅就是:“AB一下看看”。可如果目标都没确定的话,连如何写假设,如何找检验统计量都无法确定,更没办法定怎么测试了。
特别注意的是,在一次测试中,是很难兼容两个目标的。比如上边的小例子,如果想提升用户客单价,则要推的是贵的商品,要给的激励是满减型激励,让用户买贵一点。
如果想提升用户消费率,则要推的是便宜的商品,要给的激励是秒杀/超低价,让用户先买一笔再说。这两个目标是很难在一个方案A/B里兼容的。
所以小伙伴们, 千万别信了产品经理/运营“你先测测看看”的鬼话 。如果一开始目标都很混乱,那方案肯定也是四不像,测出来结果乱七八糟,也是很正常的。事前不想清楚,事后只能瞎着急。
注意2:测试方案合理性。 有些测试方案本身设计得就不合理,不是拿相似的方案测试,而是明显一个方案很优惠,一个很鸡肋;一个设计很美观,一个设计很丑陋。这种情况下测了也白测。测试方案本身,要是苹果对苹果,西瓜对西瓜才行。
注意3:测试对象相似性。 还以上边小例子举例。参与测试的用户群体,可能自身在客单价/消费力上就有差异。这种测试特征的差异,会直接导致:结果上的差异是由于客群特征,而非方案A/B造成的。这是最差的情况了,这样不但会导致测试无效,而且会误导业务判断。
四、Abtest的局限性
正是由于有以上三点要求,导致ABtest是有门槛的:
要有能力设计相近版本方案;
要有足够开发支持,把方案落地;
要有足够的用户数据,区分用户特征。
这些使得ABtest只有在数据充足、开发资源充足、业务方资源充足且没有私心的时候,才能完美落地。因此,虽然ABtest的原理很简单,但是具体应用,还需要结合实际情况做考虑。具体细节,后续再分享。
SpringBoot-Thymeleaf模板引擎整合及基本用法总结
兴趣的朋友可以去了解一下前四篇,你的赞就是对我最大的支持,感谢大家!
(一) SpringBoot起飞之路-HelloWorld
(二) SpringBoot起飞之路-入门原理分析
(三) SpringBoot起飞之路-YAML配置小结(入门必知必会)
(四) SpringBoot起飞之路-静态资源处理
说明:
- 太忙啦,同时全放到一起,后来感觉移动端篇幅太长,阅读体验太差了,就打算分成几篇来发
- 才疏学浅,就会点浅薄的知识,大家权当一篇工具文来看啦,不喜勿愤哈 ~
(一) 模板引擎引入
(1) 开发方式
在往常的开发中,一旦涉及到一个完整的前后端项目,有两种办法:
-
一种就是前后端分离,也就是说,约定好接口,通过异步方式,以 Json 字符串作为内容传递,后台进行一些业务逻辑,前台负责界面,以及通过 js 等进行数据的处理以及页面的美化。
-
还有一种方式就是模板引擎的方式,这种方式也没什么太新奇的,你可以简单的理解为 JSP 的那种模式
现在来说,前后端分离开始更加流行,但是很多旧的项目,或者自己一个人写东西,我感觉使用模板引擎也是非常不错的选择,还有时候去找一些后台的开源模板,有一些也都用了Thymeleaf, 何况出于学习的态度,学哪种技术都是可以的
针对第二种方式继续说一下,JSP 大家应该是熟悉的,比如前端传来一个 html 的页面,我们就会去将这个页面改写为 JSP 页面,我们可以用 JSP 比较容易的实现数据的显示,那么为什么不继续用 JSP 而要用别的模板引擎呢?
注:Thymeleaf 和 Freemarker 等各有特点,用熟悉后,可能会对另一种的使用方式感觉很别扭,没必要争论哪种更好,自己喜欢就行
(2) 为什么用模板引擎
以 Springboot 来说,官方就不推荐使用 JSP ,来看一下官方的解释
Springboot 2.2.7
地址:https://docs.spring.io/spring-boot/docs/2.2.7.RELEASE/reference/html/spring-boot-features.html#boot-features-jsp-limitations
Springboot:7.1.10. Template Engines
As well as REST web services, you can also use Spring MVC to serve dynamic HTML content. Spring MVC supports a variety of templating technologies, including Thymeleaf, FreeMarker, and JSPs. Also, many other templating engines include their own Spring MVC integrations.
Spring Boot includes auto-configuration support for the following templating engines:
FreeMarker、Groovy、Thymeleaf、Mustache
If possible, JSPs should be avoided. There are several known limitations when using them with embedded servlet containers.
上面一段话,总的来说,就是告诉我们,如果我们想要动态的展示HTML内容,就可以用一些模板的技术,例如 FreeMarker、Groovy、Thymeleaf、Mustache
最后有一句话说道点子上了,Springboot 建议我们尽可能的规避 JSP,并且提供了一些说法
Springboot:7.4.5. JSP Limitations
When running a Spring Boot application that uses an embedded servlet container (and is packaged as an executable archive), there are some limitations in the JSP support.
-
With Jetty and Tomcat, it should work if you use war packaging. An executable war will work when launched with java -jar, and will also be deployable to any standard container. JSPs are not supported when using an executable jar.
-
Undertow does not support JSPs.
-
Creating a custom error.jsp page does not override the default view for error handling. Custom error pages should be used instead.
第二点是说,Undertow 支持 JSP,而第三点,则是关于error错误页面的覆盖问题
前两点都不是我想说的,我们主要看第一点:当你把一个使用了 JSP 的项目打 war 包放到了 tomcat 或者其他容器中是可以运行的,当然你使用 java -jar 也是可以运行的,但是如果你直接执行 jar 包是不可以的
也就是说,打包的类型决定了JSP可不可以被正常解析使用,同时 SpringBoot 中 Tomcat 是嵌入式的,而 SpringBoot 程序往往又是脱离容器独立运行的,如果用 JSP ,就需要额外的地址去存生成的 Servlet ,可能会存在一定的安全问题,所默认是不支持jsp的
(3) SpringBoot推荐Thymeleaf吗?
网络上的文章都在传,SpringBoot 官方推荐 Thymeleaf,我看了一下 SpringBoot 2.2.7 版本的文档(看了下貌似2.3.0也挂成GA了,这部分没啥改变)我自己是没找到一个关于推荐确切的说法
Springboot:7.1.10. Template Engines
- 在Springboot——Spring Boot Features —— 7.1.10. Template Engines 中有提到,SpringBoot 提供了关于Thymeleaf 的自动装配的支持,但是同样的也提供对 FreeMarker、Groovy、Mustache 自动装配的支持,此处并没有看到推荐某一款的说法
SpringMVC:1.10.1. Thymeleaf
Thymeleaf is a modern server-side Java template engine that emphasizes natural HTML templates that can be previewed in a browser by double-clicking, which is very helpful for independent work on UI templates (for example, by a designer) without the need for a running server. If you want to replace JSPs, Thymeleaf offers one of the most extensive set of features to make such a transition easier. Thymeleaf is actively developed and maintained. For a more complete introduction, see the Thymeleaf project home page.
SpringMVC:1.10.2. FreeMarker
Apache FreeMarker is a template engine for generating any kind of text output from HTML to email and others. The Spring Framework has built-in integration for using Spring MVC with FreeMarker templates.
- 再看一下 SpringMVC 5.2.6.RELEASE 的文档,关于模板也没有提到支持或者推荐,而对于几种常见的例如 Thymeleaf、FreeMarker 等作出了一定的说明
所以,我个人觉得,SpringBoot 也只是提供了几种替代 JSP 的方案,并没有指定的推荐什么,当然了如果是我自己没注意到,大家也可以留言和我交流分享啊哈~
(4) 此部分小结
说了一大堆,就一个结论,SpringBoot 中不推荐 JSP 是肯定的,但是也没有指定推荐什么引擎模板,大家可以根据需要自行选择(FreeMarker、Groovy、Thymeleaf、Mustache)
(二) JSP 真的有点麻烦
如果,你真的想要在 SpringBoot 中使用 JSP,通过一些额外的配置,也是可以的,一起感受一下
(1) 导入依赖
初始化的时候,我选了基本的 web 还涉及到热部署的 devtools ,简单体验一下只勾选 web 也成
spring-boot-starter-web,已经内置了,spring-boot-starter-tomcat,其下有tomcat-embed-core、tomcat-embed-el 等,前者包含servlet-api和内置 servlet 容器,后者为el表达式包,所以我们体验的时候,只需要引入 jstl 以及与编译jsp相关的 tomcat-embed-jasper 就可以了
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- jsp begin-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- jsp end-->
</dependencies>
(2) 编写后台代码
实体类
首先创建一个 User实体,简单写三个成员
public class User {
private String username;
private Integer age;
private String address;
...... get set toString 方法
}
控制层
这段代码够浅显了,返回这个 List 前台遍历就行了
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/list")
public String list(Model model) {
System.out.println("查询所有");
User u1 = new User();
u1.setUsername("Steven");
u1.setAge(20);
u1.setAddress("北京");
User u2 = new User();
u2.setUsername("Jack");
u2.setAge(30);
u2.setAddress("上海");
List<User> users = new ArrayList<User>();
users.add(u1);
users.add(u2);
model.addAttribute("users", users);
return "list";
}
}
(3) 编写 JSP
首先在 main 文件夹下,创建webapp文件夹,其下创建 WEB-INF 文件夹用来存放jsp文件(我在其下又多创建了一个pages文件夹,看个人习惯就可),而静态文件夹仍然放在resources下的静态资源文件夹例如 static public 等
位置:src/main/webapp/WEB-INF/pages/list.jsp
下面也就是一个遍历到表格的 Demo
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>list all</title>
</head>
<body>
<table border="1">
<c:forEach items="${users}" var="user">
<tr>
<td>${user.username}</td>
<td>${user.age}</td>
<td>${user.address}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
(4) 修改配置
上面做完了,如果不配置的话,这些页面根本是找不到的,所以需要配置 JSP 映射路径和后缀
spring.mvc.view.prefix=/WEB-INF/pages/
spring.mvc.view.suffix=.jsp
(5) 测试
直接运行启动类访问
可以访问得到
打成 jar 运行访问
Maven Projects --> Lifecycle --> clean --> package
我们的 JSP 页面访问不到了
(6) 多模块项目404问题
主要的原因是无法找到正确的路径,所以需要将启动的Working directory设置为模块工作文件夹$MODULE_WORKING_DIR$
,
设置方式:打开 Run/Dedug Configurations,然后修改
关于 JSP 我自己也没怎么在 SpringBoot 中用过,也没细研究过,之前看到有一个篇文章有总结过关于 JSP 的坑,大家有兴趣或许可以了解一下
https://juejin.im/post/5ad21eb5f265da23945feb62
(三) 来试试 Thymeleaf
(1) 简单评价
A:优点
首先,配置很简单,SpringBoot 对于 Thymeleaf 在内的几种模板引擎,都提供了自动装配的支持,所以简单的引入依赖就可以快速使用
其次,Thymeleaf "很优雅" ,因为绑定或者控制的时候,都是以属性的方式,所以 HTML 的原有结构,没有被破坏掉,这样,就有了一个其他模板引擎所没有的优点:可以被浏览器正常渲染,也就是说,直接就可以访问(例如 JSP 是没有办法脱离容器直接访问的)
B:缺点
标签检查很严格,有时候会发疯....
表达式和标签种类繁多 {} 花括号前面不同的符号代表不同的意思,例如 ${...}
变量表达式 、 *{...}
选择表达式
如果习惯了 freemarker 这种类型的写法,写 Thymeleaf 会感觉很麻烦,因为两者的书写角度或者说思路是不同的
C:关于性能
关于性能,在 3.x 后 Thymeleaf 已经有了很大的提升,但是我查过一下数据,貌似仍不一定有那么理想,不过就我个人而言,我一个后端狗写页面,一堆乱七八糟的 js、css 各种增大开销,Thymeleaf 带来的一些影响,貌似与我和没有很大关系(菜是原罪)
(2) 引入Thymeleaf
用之前,肯定得引入相关的依赖对吧,可以去 Thymeleaf 官网,或者 Springboot 的官网,这里我更加推荐后者
Thymeleaf 官网:https://www.thymeleaf.org
Springboot 官网:
官网在 2.3.0 的版本中,你现在去看还是没有更出来Pom的,不过可以去看 2.2.7 的
Version | Name | Description | Pom |
---|---|---|---|
2.3.0 | spring-boot-starter-thymeleaf |
Starter for building MVC web applications using Thymeleaf views | 暂未提供 |
2.2.7 | spring-boot-starter-thymeleaf |
Starter for building MVC web applications using Thymeleaf views | Pom |
2.2.7 Pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
这样引入是一种方式,但是这些相关的东西,Springboot 都已经帮我们打包好了,开箱即用
所以最终的引入方式就是:
A:Pom 增加依赖
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
B:初始化时勾选相关组件
(3) 模板页面存放位置
引入了依赖之后,先确定一下页面给放哪里,前面演示的 JSP 好不折腾,又是创建 webapp WEB-INF ,又是配置,而 Thymeleaf 等模板,则可以直接将页面放到自动生成的 templates 文件夹中就可以了
具体路径为:src -- main -- resources -- template
如果想了解一下具体原因:可以去看一下 ThymeleafProperties 这个配置类,前后缀都已经指定好了
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
// 是否在呈现模板之前检查模板是否存在
private boolean checkTemplate = true;
// 是否检查模板位置是否存在
private boolean checkTemplateLocation = true;
// 在构建URL时以查看名称作为前缀
private String prefix = DEFAULT_PREFIX;
// 在构建URL时附加到视图名称的后缀
private String suffix = DEFAULT_SUFFIX;
// 要应用于模板的模板模式
private String mode = "HTML";
// 模板文件编码
private Charset encoding = DEFAULT_ENCODING;
(4) 入门程序体验
1、编写 Controller
@Controller
public class TestController {
@RequestMapping("test")
public String test(Model model){
String hello = "Hello Thymeleaf";
model.addAttribute("hello",hello);
return "test.html";
}
}
2、编写页面
页面其实可以看到就是一个普通的 html 页面,有几个不同的地方就是,上方引入了一个命名空间的约束,使用了 th:xxx 这样的语法 例如,th:text,然后 ${hello}
引入了控制层带来的 hello
接受到的数据这一行上面的注释可以不加,不过 ${hello}
应该会报红,不过也可以运行,具体原因看下面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<h2>测试成功</h2>
<!--/*@thymesVar id="hello" type="java.lang.String"*/-->
<p th:text="‘接收到的数据: ‘ + ${hello}"></p>
</body>
</html>
3、测试结果
(5) 标签报红
<p th:text="‘接收到的数据: ‘ + ${hello}"></p>
例如上面这么一行代码,如果不加任何处理,${hello}
必然是要报红的,其下会有一个红色波浪线
原因:后端 model 中虽然添加了数据,但是由于并没有运行程序,前端的文件也不知道,所以这个时候就会有红色波浪线存在了,其实正常运行是没有问题的,就是看起来很烦
解决方式有三种:
- 自动补全快捷键,自动写出上面的注释,然后自己写入类型
<!--/*@thymesVar id="hello" type="java.lang.String"*/-->
-
<!DOCTYPE html>
下加上<!--suppress ALL-->
抑制所有警告,不过需要的每个 HTML 都得加 -
在 IDEA 中,进行一个全局的忽略
(6) Thymeleaf 特点及说明
经过了一个简单的例程,很显然,大家也有一定的感觉了,而在入门程序之前,我们已经说过一些其优缺点呀,但是说实话也没啥特殊的感觉,我们正好循着这个例子,给大家简单说一说其特点还有一些注意的点
1、首先,我们谈到了,Thymeleaf "很优雅" ,因为绑定或者控制的时候,都是以属性的方式,例如下面这段
<p th:text="‘接收到的数据: ‘ + ${hello}"></p>
如果你直接在 HTML 中写 ${hello}
那肯定是会出幺蛾子的,但是 Thymeleaf 这种写法,表达式都写在了自定义属性中,所以在静态环境下表达式的内容会被当做普通字符串,浏览器就不会报错
2、同时看到, HTML 的原有结构,没有被破坏掉,一眼望去还是 HTML 的那般模样
3、根据上面的特点,所以也就使得 Thymeleaf 在静态环境下也可以直接用浏览器运行,在静态环境下 th 指令内的内容不会被识别,但是也不会报错
(四) Thymeleaf 基本语法
(1) 引入命名空间约束
可以看到我们下面都是用 th:*
这种形式书写,如果想要正常使用,就必须在 <head>
前面引入约束
<html xmlns:th="http://www.thymeleaf.org">
上面例程中已经用过了,下面开始正式的说一些常用的语法
(2) 变量语法及 th:text
A:举例说明
一个简单变量去看上面,的入门例程就行了,其实非常简单,下面我们通过对象中的变量取值来看一下
1、编写实体
首先创建两个实体,学生类和课程类,在学生类中,引用课程类
public class Student {
private String name;
private Integer age;
private Course course;
...... 省略 get set toString
}
public class Course {
private String name;
private String teacher;
...... 省略 get set toString
}
2、编写 Conteoller
@Controller
public class TestController {
@RequestMapping("testStudent")
public String testStudent(Model model){
// 两个对象 分别赋值
Student stu = new Student();
Course course = new Course();
course.setName("JavaEE课程");
course.setTeacher("杰克老师");
stu.setName("张三");
stu.setAge(25);
stu.setCourse(course);
model.addAttribute("student",stu);
return "test.html";
}
}
3、编写页面
-
我们存入的是一个学生的对象,名为 student 我们通过
${}
的格式,来获取model中的变量,代码中所用 例如:student.name
就是ognl 表达式,可以去了解一下,形式就是xxx.属性名
-
而表达式写在一个名为:
th:text
的标签属性中,叫做指令 -
一般总会出现
th:xxx
的形式,这些常见的指令,会在后面把常见的给出,现在用的这个th:text
叫做文本替换,作用就是对表达式或变量求值,然后将结果显示在其被包含的 html 标签体内,替换掉原来的文本 -
所以可以在标签中,写上一些默认值,方便静态的时候对比效果,运行后,那些文本就被后台的数据替换掉了
<p>学生姓名: <span th:text="${student.name}"></span> </p>
<p>学生年龄: <span th:text="${student.age}"></span> </p>
<p>课程名: <span th:text="${student.course.name}"></span> </p>
<p>授课老师: <span th:text="${student.course.teacher}"></span> </p>
4、执行效果
B:补充 th:utext
还有一个 th:utext
,与上面用的 th:text
很相似区别就是:
-
th:text
以纯文本显示且不解析内容里的HTML标签或元素 -
th:utext
则把整个内容当成是HTML来解析并展示,也就是说,例如取到的值为<h2>测试</h2>
会按照二级标题来进行显示
C:减少变量书写次数方式
当我们涉及到的数据过多的时候,我们每写一个就需要写一次 student,我们同样可以将其写成一个自定义的变量
-
th:object="${student}"
获取到值 -
引用时只需要通过
*{属性名}
的方式,来获取student中的这些属性
<h5 th:object="${student}">
<p>学生姓名: <span th:text="*{name}"></span> </p>
<p>学生年龄: <span th:text="*{age}"></span> </p>
<p>课程名: <span th:text="*{course.name}"></span> </p>
<p>授课老师: <span th:text="*{course.teacher}"></span> </p>
</h5>
(3) 字符串拼接表达式
上面说完了变量,但是我们还有很多时候,还有一些内容是不希望被当做变量解析的,也就是我们所说的字面值,常见的类型例如:字符串、或者数值等都是这样的,例如字符串 只需要在书写时加上单引号,就可以了,而数字不需要什么处理,直接就可以用,还可以进行算数运算
当然很多时候,我们取到的数据,要配合页面的一些字符串来进行显示,一种做法就是直接在外面写好内容例如
<p>学生姓名: <span th:text="${student.name}"></span> </p>
还有一种常见的,就是字符串与表达式的拼接,先说一下普通的方式:
- ① 把上面的 p 标签中的内容移到 th:text 中来,用单引号引入即可
<p> <span th:text=" ‘学生姓名: ‘ + ${student.name}"></span> </p>
- ② 如果涉及到大量的拼接,使用单引号拼接,书写以及可读上会很混乱,所以,可以进行简化:
<p> <span th:text=" |姓名: ${student.name}|"></span> </p>
多提一句,这种方式,在静态环境运行的时候,是没有字符串显示的,只有用 SpringBoot 运行时才可以
(4) 运算符
运算符这一块,我都是照着 Thymeleaf 官方文档 ,第4大节,Standard Expression Syntax 中写的,摘了一部分感觉还算常用的,不一定所有的例子我都给了测试,给了一些有代表性的
A:算数运算
1、支持的运算符
- 二元操作:+, - , * , / , %
- 一元操作: - (负)
2、测试代码
注意:运算符最好放在外面,因为 运算符放在了 {} 内部, 表达式使用的是 ognl 引擎进行计算;,如果运算符放在外部, 那么 表达式使用的是 thymeleaf 引擎进行计算
<p>学生年龄 = <span th:text="${student.age} "></span> </p>
<p>学生年龄 / 2 = <span th:text="${student.age} / 2 "></span> </p>
<p>学生年龄 % 2 = <span th:text="${student.age} % 2 "></span> </p>
3、执行效果
B:布尔运算
1、支持的运算符
- 一元运算符 : and, or
- 二元运算符 : !, not
C:比较运算
1、支持的运算符
- 比较:> , < , >= , <= ( gt , lt , ge , le )
- 等于:== , != ( eq , ne )
2、说明:
-
> 和 <
会被当做标签,所以不能直接使用,可以用括号内的别名代替使用 -
== 和 !=
除比较数值外,还有类似 equals 的作用
3、测试代码
<p>学生姓名: <span th:text="${student.name}"></span> </p>
<p>学生姓名为张三 = <span th:text="${student.name} == ‘张三‘"></span> </p>
<p>学生年龄为 25 = <span th:text="${student.age} == 25 "></span> </p>
<p>学生年龄 % 2 为 0 = <span th:text="${student.age} % 2 == 0 "></span> </p>
4、执行效果
D:条件运算
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
2、测试代码
<p>学生姓名 = <span th:text="${student.name} == ‘张三‘ ? ‘是张三‘:‘不是张三‘"></span> </p>
3、执行效果
(5) 条件运算再补充
上面讲条件运算是放到了运算符中,演示了一下三元运算,因为逻辑判断是非常常用的,所以我们再补充一下
A:if
没什么好说的,就是一个简单的判断
1、测试代码
<p>学生是否成年: <span th:if="${student.age} >= 18 ">成年</span> </p>
只有当年龄 > 18 岁的时候,才会显示成年这两个字,比较简单,就不测试了
B:unless
unless 与 if 是截然相反的两个概念,if 是满足条件则执行,而 unless 是不满足则执行
1、测试代码
<p>学生是否成年: <span th:unless="${student.age} >= 18 ">成年</span> </p>
自己测试感受一下
C:switch
一个分支语句的语法,也很好理解,注意:th:case="*"
表示默认,放最后面就可以了
1、测试代码
<div th:switch="${student.name}">
<p th:case="张三">姓名: 张三</p>
<p th:case="李四">姓名: 李四</p>
<p th:case="王五">姓名: 王五</p>
<p th:case="*">姓名: 未找到</p>
</div>
2、执行效果
改了下controller,把学生姓名赋值成了李四,看一下页面的显示
(6) 循环语法
循环也是非常常用的一种用法,通过 th:each
指令,来进行实现,我们下边循着一个小 Demo 学习一下
A:演示 Demo
1、创建用户类
public class User {
private String nickname;
private Integer age;
...... get set toString 方法
}
2、controller 添加方法
总之就是返回一个用户的 List 集合
@RequestMapping("testUser")
public String testUser(Model model){
User user1 = new User();
user1.setNickname("飞翔的企鹅");
user1.setAge(30);
User user2 = new User();
user2.setNickname("伤心小男孩");
user2.setAge(25);
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
model.addAttribute("userList",list);
return "test.html";
}
3、页面代码
此处注意:${userList}
中的 userLisr 就是我们返回的那个集合,user 是我们遍历到的每一个用户,和 Java 里面的增强 for 感觉是相似的
<table border="1">
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr th:each="user : ${userList}">
<td th:text="${user.nickname}">NULL</td>
<td th:text="${user.age}">0</td>
</tr>
</table>
4、执行效果
B:补充说明
① 迭代类型
关于要被遍历的值,也就例如我们上面的 ${userList}
实际上有很多种可以接受的类型
- Enumeration,枚举
- Map 集合
- List、数组及其它一切符合数组结果的对象
上面 Demo 的演示,更像 Java 中的增强 for,增强 for 虽然遍历很方便,但是也有比之普通 for 的缺点,那就是没有了例如一些状态量,例如开始结束等等,有了一定的局限,所以 Thymeleaf 给我们提供了 stat对象,帮助我们弥补这一点
② stat对象的属性
- index,当前迭代对象的index,从0开始的角标
- count,元素的个数,从1开始
- size,总元素个数
- current,当前遍历到的元素
- even/odd,布尔值,当前循环是否是偶数/奇数,boolean值
- first/last,返回是否为第一或最后,boolean值
1、测试代码
<table border="1">
<tr>
<th>昵称</th>
<th>年龄</th>
<th>index</th>
<th>count</th>
<th>size</th>
<th>current.nickname</th>
<th>even</th>
<th>odd</th>
<th>first</th>
<th>last</th>
</tr>
<tr th:each="user,userStat : ${userList}">
<td th:text="${user.nickname}">nickname</td>
<td th:text="${user.age}">age</td>
<th th:text="${userStat.index}">index</th>
<th th:text="${userStat.count}">count</th>
<th th:text="${userStat.size}">size</th>
<th th:text="${userStat.current.nickname}">current.nickname</th>
<th th:text="${userStat.even}">even</th>
<th th:text="${userStat.odd}">odd</th>
<th th:text="${userStat.first}">first</th>
<th th:text="${userStat.last}">last</th>
</tr>
</table>
2、执行效果
(五) 内置方法
(1) 环境、上下文有关
Thymeleaf 还提供了一些内置的方法,供我们调用,不过我也不推荐过多的使用下列方法,前端页面中,尽量还是减少逻辑,下面是从官方文档中截的一张图,我下面在表格中选了几个翻译了一下
对象 | 作用 |
---|---|
#ctx |
获取 Thymeleaf 自己的 Context对象 |
#requset |
是web程序的情况下,用来获取HttpServletRequest对象 |
#response |
是web程序的情况下,用来获取HttpServletReponse对象 |
#session |
是web程序的情况下,用来获取HttpSession对象 |
#servletContext |
是web程序的情况下,用来获取HttpServletContext对象 |
(2) 工具类方法
还有一些,工具性质的内置对象,方便使用,还是先看下官方的截图,当然了我没截全所有的,有需要可以自己去看一下哈
对象 | 作用 |
---|---|
#dates |
用来处理时间(java.util.date)的对象 |
#calendars |
处理日历中日期(java.util.calendar) 的对象 |
#numbers |
格式化数字 |
#strings |
处理字符串 |
#bools |
判断布尔值 |
#arrays |
处理数组 |
#lists |
处理 List 集合 |
#sets |
处理 set 集合 |
#maps |
处理 map 集合 |
(3) 演示一下
1、编写 controller 方法
@RequestMapping("testDate")
public String testDate(Model model){
model.addAttribute("currentTime",new Date());
return "test.html";
}
2、编写页面
<p>现在时间: <span th:text="${#dates.format(currentTime,‘yyyy-MM-dd hh:mm:ss‘)}">2020-05-19 00:00:00</span></p>
3、执行效果
(六) 常用标签
-
标签中只做一个类似提纲目录的用处,更详细的用法还需要进行查阅与实践
补充:
${...}
: 变量表达式*{...}
: 选择表达式#{...}
: 消息 (i18n) 表达式@{...}
: 链接 (URL) 表达式~{...}
: 片段表达式
(1) th:text
文本替换:主要用于文本的显示
第一种:
<p th:text="‘接收到的数据: ‘ + ${hello}"></p>
第二种:
<p>学生姓名: <span th:text="${student.name}"></span> </p>
(2) th:utext
支持 HTML 的文本替换,可以用于富文本编辑器编辑后的内容显示到前端的页面上
th:utext
则把整个内容当成是HTML来解析并展示,也就是说,例如取到的值为<h2>测试</h2>
会按照二级标题来进行显示
<p th:utext="‘接收到的含有HTML标签数据: ‘ + ${test}"></p>
(3) th:if / th:unless
th:if 用于判断条件,满足则执行,而 th:unless 与前者正好相反
th:if
<p>学生是否成年: <span th:if="${student.age} >= 18 ">成年</span> </p>
th:unless
<p>学生是否成年: <span th:unless="${student.age} >= 18 ">成年</span> </p>
(4) th:switch / th:case
用于多个同等级判断,即多选一
<div th:switch="${student.name}">
<p th:case="张三">姓名: 张三</p>
<p th:case="李四">姓名: 李四</p>
<p th:case="王五">姓名: 王五</p>
<p th:case="*">姓名: 未找到</p>
</div>
注意:th:case="*"
表示默认,放最后面就可以了
(5) th:each
用于遍历集合中的对象,和 JSTL 中的 <c:forEach>
基本是一致的
除此之外,还能获取到一些状态量 stat ,请翻阅上面关于循环语法的讲解
<table border="1">
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr th:each="user : ${userList}">
<td th:text="${user.nickname}">NULL</td>
<td th:text="${user.age}">0</td>
</tr>
</table>
(6) th:action
用于表单的提交时,相当于 <form>
标签的action属性。
<form th:action="@{user/login}" method="post"></form>
(7) th:src
用于引入例如图片或者 js 等外部资源
<img th:src="@{../images/test.jpg}"/>
或者
<script th:src="@{../static/register.js}"></script>
(8) th:href
用于定义超链接,相当于<a></a>
标签的href属性
传统拼接传递
<a th:href="/showStudent?id=123456&name=张三"></a>
带一个参数传递
<a th:href="@{/student/details(studentId=${student.id})}" ></a>
(9) th:value
用于属性赋值
<input th:value = "${student.name}" />
(10) th:object / th:field
th:object 和 th:field 常搭配之用,用来表单参数绑定,看一个例子就大概明白了
1、编写实体
public class LoginUser {
private String username;
private String password;
......
}
2、编写 controller 方法
@RequestMapping("/testLogin")
public String testLogin(@ModelAttribute(value = "loginUser")LoginUser loginUser, ModelMap modelMap){
String username = loginUser.getUsername();
String password = loginUser.getPassword();
System.out.println(username);
System.out.println(password);
if ("admin".equals(username) && "admin888".equals(password)){
modelMap.addAttribute("msg","登陆成功");
return "test.html";
}
modelMap.addAttribute("msg","登陆失败");
return "test.html";
}
3、编写页面
<form id="login" th:action="@{/testLogin}" th:object="${loginUser}">
<input type="text" value="" th:field="*{username}"></input>
<input type="text" value="" th:field="*{password}"></input>
<input type="submit" value="提交" />
<span th:text="${msg}"></span>
</form>
4、执行效果
(七) 结尾
如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!
如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号
在这里的我们素不相识,却都在为了自己的梦而努力 ?
一个坚持推送原创开发技术文章的公众号:理想二旬不止
以上是关于ABtest原理及用法总结的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot-Thymeleaf模板引擎整合及基本用法总结