SpringBoot - 页面模版
Java 从 0 到架构师目录:【Java从0到架构师】学习记录
Gitee 代码:https://gitee.com/szluyu99/mj_java_frame/tree/master/04_SpringBoot
SpringBoot 中常用的页面模版有:
- JSP
- Freemarker
- Thymeleaf(推荐)
集成 JSP
1、添加 JSP 解析依赖:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
2、配置视图的前缀、后缀、热部署:
# application.yml
spring:
mvc:
view:
prefix: /
suffix: .jsp
# 使得热部署生效
server:
servlet:
jsp:
init-parameters:
- development: true
3、设置工作目录 (Working directory)
- 如果是使用 spring-boot-maven-plugin 运行,可以不用设置这一项
集成 Thymeleaf
Thymeleaf 是在 SpringBoot 中推荐使用的模版引擎
- 可以完全取代 JSP
- 可以在没有服务器的环境下展示静态页面
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
一些默认配置:引入依赖以后就有,不需要自己写
spring:
thymeleaf:
encoding: UTF-8
prefix: classpath:/templates/
suffix: .html
enabled: true
servlet:
content-type: text/html
在 controller 中可以用 Model 传递数据给模版页面:
Model 参数并非 SpringBoot 独有,可以单独在 SpringMVC 中使用
@Controller
public class TestController {
@RequestMapping("/test")
public String test(Model model) {
model.setAttribute("name", "mj666");
// classpath:/templates/test.html
return "test";
}
}
在页面模版中通过 th:text="${name}"
接收后端传来的值:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:text="${name}"></div>
</body>
</html>
Thymeleaf 中属性名都是以 th:
开头:
th:text
设置的文本不会进行解析渲染(以文本的形式显示)th:utext
设置的文本会进行解析渲染(以 HTML 的形式显示)
Thymeleaf 中常用表达式:
- 变量表达式 (Variable):
${...}
- 选中变量表达式 (Selection Variable):
*{...}
- 消息表达式 (Message):
#{...}
- 链接表达式 (Link URL):
@{...}
- 片段表达式 (Fragment):
~{...}
关于静态资源的注意点:
- classpath:/templates/ 目录下的内容,默认只能通过转发访问;无法通过 URL 直接访问
- 通过 spring.resources.static-locations 设置静态资源目录,可以直接通过 URL 访问
Thymeleaf 基本语法
注释 - 3 种
示例代码:thymeleaf 注释
HTML 注释:
<!-- 注释 -->
解释器级别的注释(parser-level)
- 作为静态页面打开时,可以看见其中内容
- 只有经过 Thymeleaf 解析器处理后,才会变成真正的注释
<!--/*-->
<div>在解析器处理之前,才能看见这个div</div>
<!--*/-->
原型注释(prototype-only)
- 作为静态页面打开时,它是注释
- 经过 Thymeleaf 解释器础处理后,它是正常标签,不是注释
<!--/*/
<div>在解析器处理之后,才能看见这个div</div>
/*/-->
字面量 - ${}
示例代码:thymeleaf 字面量
后端传来的数据为字符串 name=mj
,在 th:text
中解析
- 包含空格,会解析失败:
<!-- 解析失败, 抛出异常 -->
<div th:text="I love coding."></div>
- 不包含空格会作为字符串解析:
<!-- Ilovecoding -->
<div th:text="Ilovecoding"></div>
- 使用单引号或
|
可以作为字符串解析:
<!-- I love coding. -->
<div th:text="'I love coding.'"></div>
<div th:text="|I love coding.|"></div>
- 字符串的拼接:单引号
'
和|
可以互相拼接
<!-- MJ love coding. -->
<div th:text="${name} + 'love coding.'"></div>
<div th:text="${name} + |love| + 'coding.'"></div>
- 在
|
中可以直接使用${}
,单引号'
中会作为字符串输出
<!-- MJ love coding. -->
<div th:text="|${name} love coding.|"></div>
<!-- ${name} love coding. -->
<div th:text="'${name} love coding.'"></div>
后端传来的数据为数字 age=20
,在 th:text
中解析:
<!-- 2020 -->
<div th:test="2020"></div>
<!-- 4040 -->
<div th:test="2020 * 2"></div>
<!-- 0 -->
<div th:test="${age} % 2"></div>
<!-- 布尔值 -->
<!-- true -->
<div th:text="${age} % 2 == 0"></div>
<!-- false -->
<div th:text="${age} <= 10"></div>
[[ ${} ]]
设置文本的另一种方法:使用 [[ ${xx} ]]
可以不用写在标签里
<!-- 下列写法等价 -->
<div>[[ ${name} + '666' ]]</div>
<div th:text="${name} + '666'"></div>
在 js 中要想获取后端传来的数据,或使用 thymeleaf 语法,必须用这种写法:
location.href = '[[@{/comment.html}]]'
局部变量 - th:with、三目运算
示例代码:thymeleaf 字面量
利用 th:with
可以定义局部变量:
<div th:with="isEven=${age % 2 != 0}, myName=${name}">
<div th:text="${isEven}"></div>
<div th:text="${myName}" th:class="${isEven} ? 'even' : 'odd'"></div>
</div>
三目运算的使用:
<!-- 下面两种用法等价 -->
<div th:text="${age} == null" ? 'no age' : null></div>
<div th:text="${age} == null" ? 'no age'></div>
<!-- 下列用法等价 -->
<div th:text="${age == null} ? 'no age'"></div>
<div th:text="${age != null} ? ${age} : 'no age'"></div>
<div th:text="${age} ?: 'no age'"></div>
选中变量表达式 - *{}、th:object
示例代码:thymeleaf 选中变量表达式
在 th:object
的内部只能使用 *{xx}
获取对象的属性的值
- 或者使用
*{#object.xx}
、${#object.xx}
- 其他地方
*{}
和${}
可以达到一样的效果
<ul th:object="${person}">
<li th:text="*{id}"></li>
<li th:text="*{name}"></li>
</ul>
<ul th:object="${person}">
<li th:text="*{#object.id}"></li>
<li th:text="${#object.name}"></li>
</ul>
<ul>
<li th:text="*{person.id}"></li>
<li th:text="${person.id}"></li>
</ul>
消息表达式 - #{}
示例代码:thymeleaf 消息表达式
消息表达式可以实现国际化 (internationalization,i18n) 的功能
spring:
messages:
# 关闭参考系统语言
fallback-to-system-locale: false
# 属性文件名(默认就是messages)
basename: messages
如果将 messages 文件放到了 resources/i18n/ 下,basename 如下:
新建每个国家地区对应的属性文件
- 文件名格式是:
basename_语言代码_国家地区.properties
例如:messages_zh_CN.properties、messages_en_US.properties - 语言代码+国家地区参考:https://www.douban.com/group/topic/4725265/
在 thymeleaf 中通过 #{}
取得对应语言文件中的数据
<!-- 切换浏览器的语言设置,就能看到效果 -->
<div th:text="#{login}"></div>
链接表达式 - @{}
示例消息:thymeleaf 链接表达式
当前 URL:http://localhost:8080/test/mj/link
当前 ContextPath:test
@{xxx}
:相对当前页面
<!-- http://localhost:8080/test/mj/users/login -->
<div><a th:href="@{users/login}">Page-relative</a></div>
@{/xxx}
:相对项目路径
<!-- http://localhost:8080/test/users/login -->
<div><a th:href="@{/users/login}">Context-relative</a></div>
@{~/xxx}
:相对服务器
<!-- http://localhost:8080/users/login -->
<div><a th:href="@{~users/login}">Server-relative</a></div>
@{//xxx}
:相对协议
<!-- http://baidu.com -->
<div><a th:href="@{//baidu.com}">Protocol-relative</a></div>
- 传参:
<!-- http://localhost:8080/test/users/get?id=10&name=mj&no=18 -->
<div><a th:href="@{/users/get(id=${id}, name=${name}, no=18)}">get</a></div>
<!-- http://localhost:8080/test/users/18/detail -->
<div><a th:href="@{/users/{no}/detail(no=18)}">detail</a></div>
条件判断 - th:if、th:unless、th:switch
<!-- id=20 -->
<!-- 666 -->
<div th:if="${id > 10}">666</div>
<!-- 888 -->
<div th:unless="${id <= 10}">888</div>
通过 th:switch
可以实现 if else 的功能:
<!-- name=MJ -->
<!-- div3 -->
<div th:switch="${name}">
<div th:case="'test'">div1</div>
<div th:case="${id}">div1</div>
<div th:case="*">div3</div>
</div>
遍历 - th:each
示例代码:thymeleaf 遍历
<table>
<tr>
<th>row</th>
<th>id</th>
<th>name</th>
</tr>
<tr th:each="person, status : ${persons}">
<td th:text="${status.index}"></td>
<td th:text="${person.id}"></td>
<td th:text="${person.name}"></td>
</tr>
</table>
使用 th:object
和 *{}
结合 th:each
来简化代码:
<div th:each="person : ${persons}" th:object="${person}">
<div th:text="*{id}"></div>
<div th:text="*{name}"></div>
</div>
block - th:block
th:block
相当于可以取代外层用于循环的标签:
- 一般使用外层循环效果如下:
<div>xxxx</div> <div>yyyy</div> <div>zzzz</div>
- 使用
th:block
不会将外层标签也进行循环:<div> xxxx yyyy zzzz </div>
block 使用示例:
<table>
<tr>
<th>row</th>
<th>id</th>
<th>name</th>
</tr>
<th:block th:each="person, status : ${persons}">
<td th:text="${status.index}"></td>
<td th:text="${person.id}"></td>
<td th:text="${person.name}"></td>
</th:block>
</table>
利用原型注释,使得 html 不解析 th:block
,仅仅作为 thymeleaf 才去解析:
实际上不这么写,也不会有什么影响,了解一下即可
<table>
<tr>
<th>row</th>
<th>id</th>
<th>name</th>
</tr>
<!--/*/ <th:block th:each="person, status : ${persons}"> /*/-->
<td th:text="${status.index}"></td>
<td th:text="${person.id}"></td>
<td th:text="${person.name}"></td>
<!--/*/ </th:block> /*/-->
</table>
属性设置 - th:attr
示例代码:thymeleaf 属性设置
thymeleaf 中 th:xx
完全等价于 data-th-xx
:
<!-- 下面写法等价 -->
<div th:text="${name}"></div>
<div data-th-text=${name}></div>
thymeleaf 中可以使用 th:attr
设置属性,多个属性用 ,
隔开:
<form th:attr="action=@{/user/get}, method='get'"></form>
<form data-th-attr="action=@{/user/get}, method='get'"></form>
<div class="blue red green"></div>
<div class="red"
th:attrprepend="class='blue '"
th:attrappend="class=' green'"></div>
内置对象
示例代码:thymeleaf 内置对象
内置对象 - 基础对象:进行数据传递
#ctx
#vars
#locale
#request
:HttpServletRequest#response
:HttpServletResponse#session
、session
:HttpSession#servletContext
、application
:ServletContext
内置对象 - 工具对象:封装了对各种类型数据的操作
#execInfo
、#messages
、#uris
、#conversions
#dates
、#calendars
#numbers
、#strings
、#objects
、#bools
#arrays
、#lists
、#sets
、#maps
#aggregates
、#ids
视图映射 - WebMvcConfigurer
示例代码:thymeleaf 视图映射
默认情况下,SpringBoot 会把 index 视图当做是首页 (classpath:/templates/index.html)
可以通过 WebMvcConfigurer 进行视图映射,简化 controller 代码
- 不需要仅仅为了跳转页面,就在 controller 中写一个方法
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 首页的映射
registry.addViewController("/").setViewName("login");
// 其他页面的映射
registry.addViewController("/i18n").setViewName("i18n");
registry.addViewController("/i18n/").setViewName("i18n");
}
}