Spring Boot(二)模版引擎

一、SpringBoot对静态资源的映射规则

SpringBoot 对静态资源的映射核心是 自动配置 + 约定优于配置 ,默认情况下,它会把以下目录下的静态资源(js/css/img/html 等)映射到 /** 路径(即访问 http://localhost:8080/xxx.xx 就能直接访问这些目录里的文件)。

1. 默认静态资源目录(优先级从高到低)

SpringBoot 会按以下顺序扫描静态资源目录,优先级高的目录中的文件会覆盖低优先级的同名文件:

目录路径(classpath 指项目的 resources 目录) 说明
classpath:/META-INF/resources/ 优先级最高(通常用于第三方 jar 包的静态资源)
classpath:/resources/ 项目自定义静态资源(推荐)
classpath:/static/ 默认生成的静态资源目录(最常用)
classpath:/public/ 优先级最低

示例

  • resources/static/ 下放一个 test.jpg,访问 http://localhost:8080/test.jpg 就能直接打开;
  • resources/public/ 下放同名 test.jpg,会被 static 目录的覆盖,访问时显示 static 里的文件。
2. 特殊映射:webjars

SpringBoot 支持将 webjars(前端库的 jar 包形式,如 jQuery、Vue)映射到 /webjars/** 路径。

  • 依赖示例(pom.xml):
XML 复制代码
<!-- 引入jQuery的webjars -->
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.6.0</version>
</dependency>
  • 访问路径:http://localhost:8080/webjars/jquery/3.6.0/jquery.min.js(对应 jar 包内的路径)。
3. 默认欢迎页(index.html)

SpringBoot 会自动在上述 4 个静态资源目录中查找 index.html,访问 http://localhost:8080/ 时会默认加载这个文件(优先级同静态目录)。

示例 :在 resources/static/ 下放 index.html,直接访问根路径就能打开。

4. 图标映射(favicon.ico)

默认会在静态资源目录中查找 favicon.ico,作为网站的标签页图标,无需额外配置。

二、模版引擎

1.什么是模板引擎

模板引擎(Template Engine)是连接「数据」和「静态页面」的中间件,可以理解为:

  • 你有一个「静态页面模板」(比如 success.html),里面留了一些「占位符」(比如 ${hello});
  • 你有一些动态数据(比如 Controller 里的 "zhangsan");
  • 模板引擎会把「动态数据」填充到「占位符」里,最终生成一个完整的、可直接展示的 HTML 页面返回给浏览器。

常用的模板引擎:JSP、Velocity、Freemarker、Thymeleaf

我们使用Thymeleaf

用 Thymeleaf 模板引擎,你只需要关注:

  • 页面结构写在html 里(前端语法);
  • 数据逻辑写在 Controller 里(后端逻辑);
  • 模板引擎自动完成「数据 + 模板」的融合。

2.Thymeleaf语法

2.1前置知识
  • Thymeleaf 语法都以 th: 开头(称为「Thymeleaf 指令」),写在 HTML 标签属性中;
  • 所有动态数据都通过「表达式」获取 ,核心表达式是 ${}(OGNL 表达式,和 EL 表达式类似);
  • 模板文件必须放在 resources/templates/ 下,且引入命名空间:
html 复制代码
<html lang="en" xmlns:th="http://www.thymeleaf.org">
2.2取值与文本渲染

这是最常用的语法,用于将后端数据填充到页面中。

指令 作用
th:text 渲染文本(转义 HTML 标签,转换为纯文本)
th:utext 渲染文本(不转义 HTML 标签,而是带样式)
th:value 渲染 input 框的值

实战示例:

Controller 准备数据:

java 复制代码
@Controller
public class ThymeleafController {
    @RequestMapping("/basic")
    public String basic(Model model) {
        model.addAttribute("name", "张三");
        model.addAttribute("htmlContent", "<h2>这是带标签的内容</h2>");
        return "basic";
    }
}

basic.html 模板:

html 复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
    <!-- 转义文本:输出 张三 -->
    <div th:text="${name}"></div>
    <!-- 不转义文本:显示带 h2 样式的内容 -->
    <div th:utext="${htmlContent}"></div>
    <!-- input 框赋值:value 为 张三 -->
    <input type="text" th:value="${name}">
</body>
</html>
2.3表达式

Thymeleaf 有 5 类核心表达式,覆盖所有动态场景:

表达式类型 语法 作用 示例
变量表达式 ${} 获取后端传递的变量 ${name}${user.age}
选择变量表达式 *{} 基于 th:object 取对象属性 见下方「对象取值」示例
链接表达式 @{} 生成 URL(自动拼接上下文) @{/success}@{/user/{id}(id=1)}
片段表达式 ~{} 引用公共页面片段(如页眉) ~{common :: header}
字面量表达式 '字符串'/ 数字 直接写常量 th:text="'Hello ' + ${name}"
文字国际化表达式 #{ } 从一个外部文件获取区域文字信息 #{main.title}

(1)链接表达式(最常用):

html 复制代码
<!-- 生成绝对路径:http://localhost:8084/success -->
<a th:href="@{/success}">访问success页面</a>
<!-- 带参数的URL:/user/1?name=张三 -->
<a th:href="@{/user/{id}(id=1, name=${name})}">访问用户1</a>

完整的 URL 转换规则(分步骤)

Thymeleaf 会按以下固定顺序处理这个表达式,最终生成合法的 URL:

步骤 1:替换「路径占位符」(优先处理 {} 中的变量)

  • 规则:参数列表中键名和路径占位符名一致 的键值对,会替换掉路径中的 {占位符}

  • 示例:/user/{id} + id=1 → 路径变为 /user/1

步骤 2:处理「查询参数」(剩余的键值对)

  • 规则:参数列表中未用于替换路径占位符 的键值对,会以 key=value 的形式拼接在 URL 后,? 分隔,多个参数用 & 分隔;

  • 示例:剩余参数 name=${name}(假设后端传递的 name 值是「张三」)→ 查询参数为 name=张三

步骤 3:拼接上下文路径(自动补全项目根路径)

  • 规则:如果你的 SpringBoot 项目配置了 server.servlet.context-path(比如 /myapp),Thymeleaf 会自动把上下文路径拼在 URL 最前面;如果没配置,这一步省略;

  • 示例:若上下文路径为 /myapp,则路径会变成 /myapp/user/1

步骤 4:URL 编码(自动处理特殊字符)

  • 规则:Thymeleaf 会自动对 URL 中的特殊字符(如中文、空格、& 等)进行 URL 编码(UTF-8),避免 URL 非法;

  • 示例:name=张三 → 编码为 name=%E5%BC%A0%E4%B8%89

(2)对象取值(选择变量表达式)

Controller 传递对象:

java 复制代码
// 定义User类(含id、name、age属性)
model.addAttribute("user", new User(1, "李四", 20));

模板取值:

html 复制代码
<!-- 方式1:${} 取对象属性 -->
<div th:text="${user.name}"></div>
<!-- 方式2:*{} 简化取值(需先指定 th:object) -->
<div th:object="${user}">
    <div th:text="*{name}"></div>
    <div th:text="*{age}"></div>
</div>

(3)文字国际化表达式

步骤 1:先明确国际化配置文件的规范

国际化文件必须放在 resources 目录下,命名规则是:

XML 复制代码
messages_语言代码_国家代码.properties  # 特定语言(如中文、英文)
messages.properties                    # 默认语言(找不到对应语言时用)

示例:

  • 中文配置文件:messages_zh_CN.properties
  • 英文配置文件:messages_en_US.properties
  • 默认配置文件:messages.properties
步骤 2:配置文件中定义 Key-Value

在配置文件里,main.title 就是「键」,等号后是「对应语言的文本值」:

XML 复制代码
# messages_zh_CN.properties(中文)
main.title = 我的网站首页
user.name = 用户名

# messages_en_US.properties(英文)
main.title = My Website Home
user.name = User Name

# messages.properties(默认,兜底)
main.title = Home Page
user.name = Name
步骤 3:#{main.title} 的取值规则

Thymeleaf 渲染 #{main.title} 时,会按以下优先级找值:

  1. 先判断当前请求的「语言环境」(比如浏览器设置的是中文 / 英文);
  2. 优先从对应语言的配置文件中找 main.title 的值(比如中文环境找 messages_zh_CN.properties);
  3. 如果找不到(比如没有配置日文文件,但请求是日文环境),就用 messages.properties 里的默认值;
  4. 把找到的值渲染到页面上。
2.4表达式支持的语法

字面(Literals)

  • 文本文字(Text literals): 'one text', 'Another one!',...
  • 数字文本(Number literals): 0, 34, 3.0, 12.3,...
  • 布尔文本(Boolean literals): true, false
  • 空(Null literal): null
  • 文字标记(Literal tokens): one, sometext, main,...

文本操作(Text operations)

  • 字符串连接(String concatenation): +
  • 文本替换(Literal substitutions): |The name is ${name}|,字符串拼接

算术运算(Arithmetic operations)

  • 二元运算符(Binary operators): +, -, *, /, %
  • 减号(单目运算符)Minus sign (unary operator): -

布尔操作(Boolean operations)

  • 二元运算符(Binary operators):and, or
  • 布尔否定(一元运算符)Boolean negation (unary operator):!, not

比较和等价(Comparisons and equality)

  • 比较(Comparators): >, <, >=, <= (gt, lt, ge, le)
  • 等值运算符(Equality operators):==, != (eq, ne)

条件运算符(Conditional operators)

If-then: (if) ? (then)

If-then-else: (if) ? (then) : (else)

Default: (value) ?: (defaultvalue)

2.5常用的标签

关键字 功能介绍 案例
th:id 替换id <input th:id="'xxx' + ${collect.id}"/>
th:text 文本替换 <p th:text="${collect.description}">description</p>
th:utext 支持html的文本替换 <p th:utext="${htmlcontent}">conten</p>
th:object 替换对象 <div th:object="${session.user}">
th:value 属性赋值 <input th:value="${user.name}" />
th:onclick 点击事件 th:οnclick="'getCollect()'"
th:each 属性赋值 tr th:each="user,userStat:{users}"\> * user:循环的「当前元素变量」→ 代表 `{uList}` 中当前遍历到的那个 User 对象,可自定义名称 * userStat:循环的「状态变量」→ 记录循环的索引、序号、奇偶行等状态,可选省略
th:if 判断条件 <a th:if="${userId == collect.userId}" >
th:unless 和th:if判断相反 <a th:href="@{/login}" rel="external nofollow" rel="external nofollow" rel="external nofollow" th:unless=${session.user != null}>Login</a>
th:href 链接地址 <a th:href="@{/login}" rel="external nofollow" rel="external nofollow" rel="external nofollow" th:unless=${session.user != null}>Login</a> />
th:switch 多路选择 配合th:case 使用 <div th:switch="${user.role}">
th:case th:switch的一个分支 <p th:case="'admin'">User is an administrator</p>
th:fragment 布局标签,定义一个代码片段,方便其它地方引用 <div th:fragment="alert">
th:include 布局标签,替换内容到引入的文件 <head th:include="layout :: htmlhead" th:with="title='xx'"></head> />
th:replace 布局标签,替换整个标签到引入的文件 <div th:replace="fragments/header :: title"></div>
th:selected selected选择框 选中 th:selected="({xxx.id} == {configObj.dd})"
th:src 图片类地址引入 <img class="img-responsive" alt="App Logo" th:src="@{/img/logo.png}" />
th:action 表单提交的地址 <form action="subscribe.html" th:action="@{/subscribe}">
userStat的常用属性(必记)
属性 含义 示例(遍历 3 个用户)
userStat.index 循环索引(从 0 开始) 0、1、2
userStat.count 循环序号(从 1 开始) 1、2、3
userStat.size 列表总长度(固定值) 3(始终不变)
userStat.even 是否为偶数行(布尔) false、true、false
userStat.odd 是否为奇数行(布尔) true、false、true
userStat.first 是否为第一个元素 true、false、false
userStat.last 是否为最后一个元素 false、false、true

3.标签测试

html:

html 复制代码
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Title</title>
</head>
<body>
<div  th:text="${hello}" th:id="${hello.toUpperCase()}">xxxx</div>
<input th:value="${user.getUsername()}">
<hr>
<div th:object="${user}">
    <span th:text="*{username}"></span>
</div>

<!--只有当 user.getAge() 返回值等于 2 时,整个 <a> 标签才会被渲染到页面上,表示链接无跳转地址(点击后不会跳转)-->
<a th:href="@{/index}" th:if="${user.getAge() == 2}" >首页</a>

<a  th:class="${user.getAge() > 2}?'class1':'class2'" >年龄</a>

<p th:if="${user.score >= 60 and user.score < 85}">B</p>
<p th:if="${user.score < 60}">C</p>
<p th:if="${user.score > 85}">优秀</p>


<span th:switch="${user.gender}">
    <p th:case="1">男</p>
    <p th:case="2">女</p>
</span>

<!--a 就是当前这一轮循环对应的 User 对象。-->
<!--aState 也是你自定义的变量名,它是 Thymeleaf 自动生成的「循环状态对象」,用来记录当前循环的各种状态信息(比如索引、序号、是否是第一行 / 最后一行等),是循环的「辅助工具」。-->
<table>

    <tr th:each="a,aState:${uList}">
        <td th:text="${a.username}"></td>
        <td th:text="${a.password}"></td>
        <td th:text="${aState.index}"></td>
    </tr>
</table>

</body>

</html>
<style>
    .class1 { color: red; /* 红色文字 */ }
    .class2 { color: blue; /* 蓝色文字 */ }
</style>

Controller中给数据:

java 复制代码
    @RequestMapping("/success")

    public String hello(HttpServletRequest req, HttpSession httpSession, Model model){
        model.addAttribute("hello","<h1>renliang</h1>");
        User user = new User();
        user.setPassword("111");
        user.setUsername("renliang");
        user.setAge(2);
        user.setScore(78);
        user.setGender(2);
        List<User> uList = new ArrayList<>();
        for (int i = 0; i < 10; i++){
            User u = new User();
            u.setUsername("renliang"+i);
            u.setPassword("111"+i);

            uList.add(u);
        }

        // httpSession.setAttribute("user", user);
        model.addAttribute("user", user);
        model.addAttribute("uList", uList);
        return "success";
    }
    @RequestMapping("/index")
    public String index() {
        // 返回模板名称:对应 resources/templates/index.html
        return "index";
    }
相关推荐
茶本无香2 小时前
设计模式之七—装饰模式(Decorator Pattern)
java·设计模式·装饰器模式
rannn_1112 小时前
【Javaweb学习|Day11】SpringBoot原理|配置优先级、Bean的管理、原理及源码分析
java·spring boot·后端·学习·javaweb
晚风吹长发2 小时前
初步了解Linux中的信号保存和简单使用
linux·运维·服务器·数据结构·后端·算法
马猴烧酒.2 小时前
智能协图云图库学习笔记day5
java·jvm·spring boot·笔记·学习·mvc
2501_933513042 小时前
Java后端开发者的AGI时代学习与职业路径策略
java·学习·agi
lixin5565562 小时前
基于迁移学习的图像分类增强器
java·人工智能·pytorch·python·深度学习·语言模型
计算机学姐2 小时前
基于SpringBoot的校园跑腿系统【数据可视化统计+原创精品】
java·vue.js·spring boot·后端·mysql·信息可视化·echarts
va学弟2 小时前
Java 网络通信编程(1):服务器多任务连接+广播消息实现
java·运维·服务器
独自破碎E3 小时前
【双指针+字符串】字符串变形
android·java