Spring基础知识(28)- Spring Boot (九)
静态资源映射、Thymeleaf 模板、整合 Thymeleaf
1. 静态资源映射
在 Web 应用中会涉及到大量的静态资源,例如 JS、CSS 和 HTML 等。我们知道,Spring MVC 导入静态资源文件时,需要配置静态资源的映射;但在 SpringBoot 中则不再需要进行此项配置,因为 SpringBoot 已经默认完成了这一工作。
Spring Boot 默认为我们提供了 3 种静态资源映射规则:
(1) 默认资源映射
(2) 静态首页(欢迎页)映射
(3) WebJars 映射
1) 默认资源映射
当访问项目中的任意资源(即“/**”)时,Spring Boot 会默认从以下路径中查找资源文件(优先级依次降低):
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
这些路径又被称为静态资源文件夹。
当请求某个静态资源时,Spring Boot 会先查找优先级高的文件夹,再查找优先级低的文件夹,直到找到指定的静态资源为止。
示例,在 “” 里 SpringbootWeb 项目基础上,代码如下。
创建 src/main/resources/static/test.html 文件
1 2 3 4 "Content-Type" content="text/html; charset=UTF-8"/> 5 6 7Test HTML Page
8 9
访问:http://localhost:9090/test.html
2) 静态首页(欢迎页)映射
静态资源文件夹下的所有 index.html 被称为静态首页或者欢迎页,它们会被 /** 映射,当访问 "/" 或者 "/index.html" 时,都会跳转到静态首页(欢迎页)。
注意,访问静态首页或欢迎页时,其查找顺序也遵循默认静态资源的查找顺序,即先查找优先级高的目录,在查找优先级低的目录,直到找到 index.html 为止。
示例,在上文 SpringbootWeb 项目基础上,代码如下。
创建 src/main/resources/static/index.html 文件
1 2 3 4 "Content-Type" content="text/html; charset=UTF-8"/> 5 6 7Index HTML Page
8 9
访问:http://localhost:9090/ 或 http://localhost:9090/index.html
3)WebJars 映射
为了让页面更加美观,让用户有更多更好的体验,Web 应用中通常会使用大量的 JS 和 CSS,例如 jQuery、Bootstrap 等等。通常我们会将这些 Web 前端资源拷贝到 Java Web 项目的 webapp 相应目录下进行管理。但是 Spring Boot 项目是以 JAR 包的形式进行部署的,不存在 webapp 目录,那么 Web 前端资源该如何引入到 Spring Boot 项目中呢?
WebJars 可以完美的解决上面的问题,它可以 Jar 形式为 Web 项目提供资源文件。
WebJars 可以将 Web 前端资源(JS,CSS 等)打成一个个的 Jar 包,然后将这些 Jar 包部署到 Maven 中央仓库中进行统一管理,当 Spring Boot 项目中需要引入 Web 前端资源时,只需要访问 WebJars 官网 (https://www.webjars.org/),找到所需资源的 pom 依赖,将其导入到项目中即可。
所有通过 WebJars 引入的前端资源都存放在当前项目类路径(classpath)下的 "/META-INF/resources/webjars/" 目录中。
示例,在上文 SpringbootWeb 项目基础上,代码如下。
(1) 修改 pom.xml,将 jquery 引入到该项目中
... ... ...... org.webjars jquery 3.6.0
(2) 修改 src/main/resources/static/test.html 文件
1 2 3 4 "Content-Type" content="text/html; charset=UTF-8"/> 5 <script language="javascript" src="/webjars/jquery/3.6.0/jquery.js"></script> 6 7 8Test HTML Page
9 10 <script type="text/javascript"> 11 $(document).ready(function() { 12 console.log("Test HTML Page"); 13 }); 14 </script> 15 16
访问:http://localhost:9090/test.html
2. Thymeleaf 模板
Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。它与 JSP,Velocity,FreeMaker 等模板引擎类似,也可以轻易地与 Spring MVC 等 Web 框架集成。
Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为“.html”,因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果;当通过 Web 应用程序访问时,Thymeleaf 会动态地替换掉静态内容,使页面动态显示。
Thymeleaf 模板引擎具有以下特点:
(1) 动静结合:Thymeleaf 既可以直接使用浏览器打开,查看页面的静态效果,也可以通过 Web 应用程序进行访问,查看动态页面效果;
(2) 开箱即用:Thymeleaf 提供了 Spring 标准方言以及一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能;
(3) 多方言支持:它提供了 Thymeleaf 标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式;必要时,开发人员也可以扩展和创建自定义的方言;
(4) 与 SpringBoot 完美整合:SpringBoot 为 Thymeleaf 提供了的默认配置,并且还为 Thymeleaf 设置了视图解析器,因此 Thymeleaf 可以与 Spring Boot 完美整合。
Thymeleaf 通过在 html 标签中,增加额外属性来达到“模板+数据”的展示方式,示例代码如下。
1 2 "en" xmlns:th="http://www.thymeleaf.org"> 3 4 "UTF-8"> 5Title 6 7 8 9"Welcome Thymeleaf">Welcome Static HTML
10 11
当直接使用浏览器打开时,浏览器显示:
欢迎您访问静态页面HTML
当通过 Web 应用程序访问时,浏览器显示:
迎您来到 Thymeleaf
1) Thymeleaf 语法规则
在使用 Thymeleaf 之前,首先要在页面的 html 标签中声明名称空间,示例代码如下。
xmlns:th="http://www.thymeleaf.org"
在 html 标签中声明此名称空间,可避免编辑器出现 html 验证错误,但这一步并非必须进行的,即使我们不声明该命名空间,也不影响 Thymeleaf 的使用。
Thymeleaf 作为一种模板引擎,它拥有自己的语法规则。Thymeleaf 语法分为以下 2 类:
(1) 标准表达式语法
(2) th 属性
2) 标准表达式语法
Thymeleaf 模板引擎支持多种表达式:
变量表达式:${...}
选择变量表达式:*{...}
链接表达式:@{...}
国际化表达式:#{...}
片段引用表达式:~{...}
(1) 变量表达式,格式如下:
使用 ${} 包裹的表达式被称为变量表达式,该表达式具有以下功能:
获取对象的属性和方法
使用内置的基本对象
使用内置的工具对象
a) 获取对象的属性和方法,表达式如下:
${person.lastName}
获取 person 对象的 lastName 属性
b) 使用内置的基本对象
Thymeleaf 中常用的内置基本对象如下:
#ctx :上下文对象;
#vars :上下文变量;
#locale:上下文的语言环境;
#request:HttpServletRequest 对象(仅在 Web 应用中可用);
#response:HttpServletResponse 对象(仅在 Web 应用中可用);
#session:HttpSession 对象(仅在 Web 应用中可用);
#servletContext:ServletContext 对象(仅在 Web 应用中可用)。
例如,可以通过以下 2 种形式,都可以获取到 session 对象中的 map 属性:
${#session.getAttribute('map')}
${session.map}
c) 使用内置的工具对象
除了能使用内置的基本对象外,变量表达式还可以使用一些内置的工具对象。
strings:字符串工具对象,常用方法有:equals、equalsIgnoreCase、length、trim、toUpperCase、toLowerCase、indexOf、substring、replace、startsWith、endsWith,contains 和 containsIgnoreCase 等;
numbers:数字工具对象,常用的方法有:formatDecimal 等;
bools:布尔工具对象,常用的方法有:isTrue 和 isFalse 等;
arrays:数组工具对象,常用的方法有:toArray、length、isEmpty、contains 和 containsAll 等;
lists/sets:List/Set 集合工具对象,常用的方法有:toList、size、isEmpty、contains、containsAll 和 sort 等;
maps:Map 集合工具对象,常用的方法有:size、isEmpty、containsKey 和 containsValue 等;
dates:日期工具对象,常用的方法有:format、year、month、hour 和 createNow 等。
例如,可以使用内置工具对象 strings 的 equals 方法,来判断字符串与对象的某个属性是否相等,代码如下。
${#strings.equals('Tester',name)}
(2) 选择变量表达式
选择变量表达式与变量表达式功能基本一致,只是在变量表达式的基础上增加了与 th:object 的配合使用。
当使用 th:object 存储一个对象后,就可以在其子元素中使用选择变量表达式获取该对象中的属性,其中,"*" 代表该对象。
firstname
th:object 用于存储一个临时变量,该变量只在该标签及其子元素中有效。
(3) 链接表达式
不管是静态资源的引用,还是 form 表单的请求,凡是链接都可以用链接表达式。
链接表达式的形式结构如下:
无参请求:@{/xxx}
有参请求:@{/xxx(k1=v1,k2=v2)}
例如使用链接表达式引入 css 样式表,代码如下。
(4) 国际化表达式
消息表达式一般用于国际化的场景。结构如下。
th:text="#{msg}"
(5) 片段引用表达式
片段引用表达式用于在模板页面中引用其他的模板片段,该表达式支持以下 2 种语法结构:
推荐:~{templatename::fragmentname}
支持:~{templatename::#id}
以上语法结构说明如下:
a) templatename:模版名,Thymeleaf 会根据模版名解析完整路径:/resources/templates/templatename.html,要注意文件的路径;
b) fragmentname:片段名,Thymeleaf 通过 th:fragment 声明定义代码块,即:th:fragment="fragmentname";
c) id:HTML 的 id 选择器,使用时要在前面加上 # 号,不支持 class 选择器。
3) th 属性
Thymeleaf 还提供了大量的 th 属性,这些属性可以直接在 HTML 标签中使用,其中常用 th 属性及其示例如下表。
属性 描述
th:id 替换 HTML 的 id 属性,例如
th:text 文本替换,转义特殊字符,例如
hello
th:utext 文本替换,不转义特殊字符,例如
th:object 在父标签选择对象,子标签使用 *{…} 选择表达式选取值。
th:value 替换 value 属性,例如
th:with 局部变量赋值运算,例如
th:style 设置样式,例如
th:onclick 点击事件,例如
th:each 遍历,支持 Iterable、Map、数组等。
th:if 根据条件判断是否需要展示此标签,例如
th:unless 和 th:if 判断相反,满足条件时不显示,例如
th:switch 与 Java 的 switch case语句类似,通常与 th:case 配合使用,根据不同的条件展示不同的内容
th:fragment 模板布局,类似 JSP 的 tag,用来定义一段被引用或包含的模板片段,例如
th:insert 布局标签;将使用 th:fragment 属性指定的模板片段(包含标签)插入到当前标签中,例如
th:replace 布局标签;使用 th:fragment 属性指定的模板片段(包含标签)替换当前整个标签,例如
th:selected select 选择框选中
th:src 替换 HTML 中的 src 属性,例如
th:inline 内联属性;该属性有 text、none、javascript 三种取值,在 <script> 标签中使用时,js 代码中可以获取到后台传递页面的对象,例如 <script th:inline="javascript"></script>
th:action 替换表单提交地址,例如
4) Thymeleaf 公共页面提取
在 Web 项目中,通常会存在一些公共页面片段(重复代码),例如头部导航栏、侧边菜单栏和公共的 js css 等。我们一般会把这些公共页面片段提取出来,存放在一个独立的页面中,然后再由其他页面根据需要进行引用,这样可以消除代码重复,使页面更加简洁。
(1) 提取公共页面
Thymeleaf 支持公共页面的提取和引用,可以将公共页面片段提取出来,存放到一个独立的页面中,并使用 Thymeleaf 提供的 th:fragment 属性为这些提取出来的公共页面片段命名。
示例
页面片段存放在 common.html 中,代码如下。
Common code
(2) 引用公共页面
在 Thymeleaf 中,我们可以使用以下 3 个属性,将公共页面片段引入到当前页面中。
th:insert:将代码块片段整个插入到使用了 th:insert 属性的 HTML 标签中;
th:replace:将代码块片段整个替换使用了 th:replace 属性的 HTML 标签中;
th:include:将代码块片段包含的内容插入到使用了 th:include 属性的 HTML 标签中。
使用上 3 个属性引入页面片段,都可以通过以下 2 种方式实现。
~{templatename::selector}:模板名::选择器
~{templatename::fragmentname}:模板名::片段名
通常情况下,~{} 可以省略,其行内写法为 [[~{...}]] 或 [(~{...})],其中 [[~{...}]] 会转义特殊字符,[(~{...})] 则不会转义特殊字符。
示例
在页面 fragment.html 中引入 common.html 中声明的页面片段,可以通过以下方式实现。
1 2"common::fragment-name">3 4"common::#fragment-id">5 6 7"common::fragment-name">8 9"common::#fragment-id">10 11 12"common::fragment-name">13 14"common::#fragment-id">
(3) 传递参数
Thymeleaf 在提取和引入公共页面片段时,还可以进行参数传递,可以通过以下 2 种方式,将参数传入到被引用的页面片段中:
模板名::选择器名或片段名(参数1=参数值1, 参数2=参数值2)
模板名::选择器名或片段名(参数值1, 参数值2)
注:
若传入参数较少时,一般采用第二种方式,直接将参数值传入页面片段中;
若参数较多时,建议使用第一种方式,明确指定参数名和参数值。
示例
修改 fragment.html,代码如下。
1 2"common::fragment-name(var1='insert-name',var2='insert-name2')">3 4"common::#fragment-id(var1='insert-id',var2='insert-id2')">5 6 7"common::fragment-name(var1='replace-name',var2='replace-name2')">8 9"common::#fragment-id(var1='replace-id',var2='replace-id2')">10 11 12"common::fragment-name(var1='include-name',var2='include-name2')">13 14"common::#fragment-id(var1='include-id',var2='include-id2')">
修改 common.html,代码如下。
1 2"fragment-name(var1, var2)" id="fragment-id"> 3"'var1: ' + ${var1} + ', var2: ' + ${var2}">Empty
4
3. 整合 Thymeleaf
Spring Boot 推荐使用 Thymeleaf 作为其模板引擎。SpringBoot 为 Thymeleaf 提供了一系列默认配置,项目中一但导入了 Thymeleaf 的依赖,相对应的自动配置 (ThymeleafAutoConfiguration) 就会自动生效,因此 Thymeleaf 可以与 Spring Boot 完美整合 。
Spring Boot 通过 ThymeleafAutoConfiguration 自动配置类对 Thymeleaf 提供了一整套的自动化配置方案,该自动配置类的部分源码如下。
1 @Configuration(proxyBeanMethods = false) 2 @EnableConfigurationProperties({ThymeleafProperties.class}) 3 @ConditionalOnClass({TemplateMode.class, SpringTemplateEngine.class}) 4 @AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class}) 5 public class ThymeleafAutoConfiguration { 6 7 }
ThymeleafAutoConfiguration 使用 @EnableConfigurationProperties 注解导入了 ThymeleafProperties 类,该类包含了与 Thymeleaf 相关的自动配置属性,其部分源码如下。
1 @ConfigurationProperties(prefix = "spring.thymeleaf") 2 public class ThymeleafProperties { 3 private static final Charset DEFAULT_ENCODING; 4 public static final String DEFAULT_PREFIX = "classpath:/templates/"; 5 public static final String DEFAULT_SUFFIX = ".html"; 6 private boolean checkTemplate = true; 7 private boolean checkTemplateLocation = true; 8 private String prefix = "classpath:/templates/"; 9 private String suffix = ".html"; 10 private String mode = "HTML"; 11 private Charset encoding; 12 private boolean cache; 13 14 ... 15 }
ThymeleafProperties 通过 @ConfigurationProperties 注解将配置文件(application.properties/application.yml) 中前缀为 spring.thymeleaf 的配置和这个类中的属性绑定。
在 ThymeleafProperties 中还提供了以下静态变量:
DEFAULT_ENCODING:默认编码格式
DEFAULT_PREFIX:视图解析器的前缀
DEFAULT_SUFFIX:视图解析器的后缀
与 Spring Boot 其他自定义配置一样,我们可以在 application.properties/application.yml 中修改以 spring.thymeleaf 开始的属性,以实现修改 Spring Boot 对 Thymeleaf 的自动配置。
Spring Boot 整合 Thymeleaf 模板引擎,需要以下步骤:
引入依赖
创建模板文件,并放在在指定目录下
1) 引入依赖
修改 pom.xml:
12 ... 3 4 ... 5 6 7 14 15 ... 168 11 12 ... 13org.springframework.boot 9spring-boot-starter-thymeleaf 10
在IDE中项目列表 -> SpringbootWeb -> 点击鼠标右键 -> Maven -> Reload Project
2) 创建模板文件
根据 ThymeleafProperties 配置属性可知,Thymeleaf 模板的默认位置在 resources/templates 目录下,默认的后缀是 html,即只要将 HTML 页面放在“classpath:/templates/”下,Thymeleaf 就能自动进行渲染。
示例,在 “” 里 SpringbootWeb 项目基础上,修改如下。
(1) 创建 src/main/resources/templates/demo.html 文件
1 2 "en" xmlns:th="http://www.thymeleaf.org"> 3 4 "UTF-8"> 5Demo 6 7 8 9"'Welcome ' + ${name}">
10 11
(2) 创建 src/main/java/com/example/controller/IndexController.java 文件
1 package com.example.controller; 2 3 import java.util.Map; 4 import org.springframework.stereotype.Controller; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 7 @Controller 8 public class IndexController { 9 @RequestMapping("/demo") 10 public String demo(Mapmap) { 11 map.put("name", "admin"); 12 return "demo"; 13 } 14 }
注:src/main/java/com/example/controller 目录不存在,手动创建各级目录,下同。
浏览器访问 http://localhost:9090/demo,页面显示:
Welcome admin