- 参考文档:Spring Boot入门(18):使用Spring Boot快速实现静态资源映射,完美展示前端页面!
- 参考文档:Spring Boot入门(19):Spring Boot 整合 Thymeleaf 模板引擎,开发Web页面 超级详细,建议收藏
目录
静态资源映射
一句话理解静态资源映射
静态资源映射,就是给 URL 路径和文件路径建立一条对应关系。
浏览器请求 http://localhost:8080/1.jpg,框架按约定去某个目录下找 1.jpg,找到就返回文件,找不到就 404。
这条“URL → 文件地址”的规则就是静态资源映射。它不参与业务逻辑,只负责让图片、JS、CSS、HTML 这些不需要经过 Java 代码处理的文件能被浏览器访问到。
区别于接口:/user/1 走 Controller,返回 JSON;/1.jpg 不经过 Controller,直接由框架把文件流丢回去。
它解决了什么问题
-
jar 包部署没有
webapp目录
传统 war 项目把前端资源放在webapp下,容器会自动提供访问。Spring Boot 打成 jar 运行,没有这个概念,必须有新机制告诉框架资源在哪——这就是 WebJars 和默认资源目录存在的原因。 -
不再手写 XML 配置
Spring MVC 时代要写<mvc:resources mapping="/static/**" location="/static/"/>,漏配就 404。Spring Boot 通过自动配置把默认规则注册好了,开箱即用。 -
前端依赖也能用 Maven 管版本
WebJars 把 jQuery、Bootstrap 打成 jar,版本跟后端依赖一起管理,不用手工下载 JS 文件往项目里塞。 -
一个 jar 就能跑起带页面的应用
小型项目、后台管理页、内网工具,不需要额外搭 Nginx,页面放static目录跟着 jar 一起发布。 -
外部文件也能通过 HTTP 访问
用户上传的图片存在磁盘上,加一条file:映射就能用 URL 访问,不用把文件塞进 classpath。
Spring Boot 的三条默认规则
| 规则 | URL 前缀 | 去哪里找 |
|---|---|---|
| WebJars 映射 | /webjars/** |
classpath:/META-INF/resources/webjars/ |
| 默认资源目录 | /** |
依次找 META-INF/resources → resources → static → public |
| 欢迎页 | /、/index.html |
上述目录中的 index.html |
其中默认资源目录的优先级最容易踩坑:同名文件只会命中优先级最高的那一份,低优先级目录里的会被“遮住”。改了 public 里的文件却不生效,多半是 static 里还有一份同名的。
只要把 index.html 放进 static 或 public,访问 / 就能看到页面,这就是所谓“欢迎页”。
一次请求是怎么找到文件的
浏览器 GET /1.jpg
│
▼
DispatcherServlet
│ 不匹配任何 @RequestMapping,交给资源处理器
▼
ResourceHttpRequestHandler(映射 /**)
│ 按 META-INF/resources → resources → static → public 顺序查找
▼
找到文件:返回文件流 找不到:404
关键点:静态资源请求不会进入 Controller,而是由 Spring MVC 内置的资源处理器直接响应,所以它既不影响业务接口,也不需要写任何 Java 代码。
实际开发中是否常用
要分两层看:机制本身几乎每个项目都在用,但“手动配”的场景已经很少。
| 场景 | 常用程度 | 说明 |
|---|---|---|
| 前后端分离项目 | 较少 | 前端单独部署,后端只提供接口;静态资源交给 Nginx / CDN |
| 单体项目、后台管理系统 | 常用 | 页面和 JS/CSS 放 static,一个 jar 部署,省事 |
| 接口文档、监控页 | 常用(间接) | Swagger UI、Knife4j、Druid 监控页的页面,本身就走静态资源机制 |
| WebJars | 很少 | 现在有 npm、CDN,新项目基本不用,多见于老项目和教学 |
欢迎页 index.html |
偶尔 | 演示项目、内网工具首页 |
| 上传文件访问 | 常用 | 用 addResourceHandlers 映射磁盘目录 |
结论:
- 你通常感知不到它,因为 Spring Boot 默认就开了,把文件丢进
static就能访问。 - 真正需要自己动手配的只有两类:一是映射磁盘上的上传目录,二是自定义资源路径。
- 生产环境里,静态资源大多由 Nginx 或 CDN 承担,Spring Boot 负责静态资源主要出现在单体部署、内部系统和小项目中。
- WebJars 属于“了解即可”的知识点,面试可能问,实际新项目很少用。
什么时候需要自己配
- 默认四个目录不够用:用
spring.web.resources.static-locations指定新目录,注意这是覆盖默认值,不是追加。 - 访问磁盘上的文件:实现
WebMvcConfigurer#addResourceHandlers,把/img/**映射到file:/data/images/。 - 需要缓存或权限控制:通过
spring.web.resources.cache.period设置缓存时间;需要鉴权时配合拦截器处理,别把上传目录直接裸奔在公网。
# 修改默认静态资源目录(会覆盖默认的四个目录)
spring.web.resources.static-locations=classpath:/mystatic/
# 静态资源缓存时间
spring.web.resources.cache.period=3600s
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**")
.addResourceLocations("file:/data/images/");
}
}
整合 Thymeleaf 模板引擎
一句话理解 Thymeleaf
Thymeleaf 是一个服务器端模板引擎,负责把「业务数据」填进「HTML 模板」里,再由服务器吐出完整的页面。
之前用 @RestController 时,方法返回的是对象,Spring 自动转成 JSON;而返回 HTML 页面需要另一套机制:Controller 把数据放进 Model,模板引擎按模板里的占位符替换成真实数据,最终生成浏览器能显示的 HTML。这条「模板 + 数据 → 页面」的链路就是模板引擎的职责。
它和静态资源映射是两回事:静态资源映射返回的是磁盘/classpath 上现成的文件;模板引擎返回的是运行时动态拼接出来的页面。
它解决了什么问题
-
接口返回 JSON,不返回页面
前后端不分离的老式项目中,浏览器要的是 HTML。@RestController只会把返回值写成 JSON 字符串,页面根本渲染不出来,需要模板引擎承接「数据 → HTML」。 -
JSP 的局限
JSP 依赖 Servlet 容器、打成 jar 跑起来麻烦、页面里混入大量<% %>标签不美观。Thymeleaf 是纯 Java 类库,模板本身就是合法的 HTML,可以直接用浏览器打开预览。 -
前端与后端数据解耦
模板引擎把「显示(UI)」和「内容(业务数据)」分开,改样式不用动 Java 代码,改数据逻辑不用动页面,美工给的页面拿来就能套。 -
一个 jar 直接跑带页面的应用
模板放在classpath:/templates/下,页面与后端一起打包发布,适合后台管理、内部工具这类单体项目。
Spring Boot 支持的模板引擎
| 模板引擎 | 官方支持程度 | 说明 |
|---|---|---|
| Thymeleaf | 最好 | Spring 官方推荐,模板即合法 HTML,可静态预览 |
| FreeMarker | 最好 | 老牌模板引擎,同样有 starter 支持 |
| Velocity | 一般 | 较老,新项目基本不用 |
| JSP | 差 | 依赖容器、jar 打包困难,Spring Boot 不推荐 |
选型建议:新项目直接用 Thymeleaf;FreeMarker 次之;JSP 尽量别用。
Thymeleaf 的核心特点
- 自然模板:模板文件本身就是标准 HTML,用浏览器直接打开也能看到静态效果,方便前端先做原型。
- 双方言:提供「标准方言」和「Spring 标准方言」,可直接套用 JSTL、OGNL 的表达式写法,省去改标签的麻烦。
- 与 SpringMVC 无缝集成:表单绑定、属性编辑器、国际化等能力开箱即用。
- 离线可预览:有网无网都能执行,页面更简洁。
集成四步走
第 1 步:引入依赖
<!-- 模板引擎 Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
第 2 步:编写 Controller(返回视图名)
@Controller
@RequestMapping("/page")
public class ThymeleafController {
@Autowired
private ILogInfoService logInfoService;
@RequestMapping("/logList.html")
public String getArticles(Model model) {
// 查询日志列表
List<LogInfo> list = logInfoService.list();
// 存入数据,模板中用 ${list} 取出
model.addAttribute("list", list);
// 返回视图名,对应 templates/logList.html
return "logList";
}
}
第 3 步:编写模板页面
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
</head>
<body>
<h6>Thymeleaf 模板引擎</h6>
<table border="1" bgcolor="#f0ffff">
<thead>
<tr>
<th>序号</th>
<th>日志内容</th>
<th>ip号</th>
<th>创建时间</th>
</tr>
</thead>
<!-- th:each 循环遍历,和 Vue 的 v-for 类似 -->
<tbody th:each="logList : ${list}">
<tr>
<td th:text="${logList.id}"></td>
<td th:text="${logList.content}"></td>
<td th:text="${logList.ip}"></td>
<td th:text="${logList.createTime}"></td>
</tr>
</tbody>
</table>
</body>
</html>
第 4 步:配置模板参数
# thymeleaf 配置
spring:
thymeleaf:
cache: false
mode: LEGACYHTML5
prefix: classpath:/templates/
suffix: .html
| 参数 | 作用 | 建议 |
|---|---|---|
cache |
是否缓存模板 | 开发环境 false(改模板不用重启),线上 true 提性能 |
mode |
模板类型 | 用 HTML5 写法时配置为对应 HTML5 模式 |
prefix |
模板所在目录 | 默认 classpath:/templates/ |
suffix |
模板后缀 | 默认 .html |
一次页面请求是怎么渲染的
浏览器 GET /page/logList.html
│
▼
DispatcherServlet
│ 匹配 @RequestMapping("/page/logList.html")
▼
ThymeleafController#getArticles
│ 查询数据 → 放入 Model → 返回视图名 "logList"
▼
ThymeleafViewResolver
│ prefix + "logList" + suffix = classpath:/templates/logList.html
▼
模板引擎替换 th:* 占位符 → 生成完整 HTML
│
▼
返回给浏览器渲染
关键点:Controller 返回的不是数据本身,而是视图名;真正的 HTML 是模板引擎在服务端渲染完成后才产生的。
Thymeleaf 常用语法
th:* 属性就是 Thymeleaf 的标签语法,按执行优先级从 1 到 8 排列,数字越小优先级越高。
| 属性 | 作用 | 优先级 |
|---|---|---|
th:insert |
代码块引入(还有 th:replace、th:include),常用于抽取公共片段 |
order=1(最高) |
th:each |
遍历循环,和 th:text / th:value 一起使用 |
order=2 |
th:if |
条件判断(还有 th:unless、th:switch、th:case) |
order=3 |
th:object |
声明表单绑定对象,配合 *{} 使用 |
order=4 |
th:attr |
修改任意属性(还有 th:attrappend、th:attrprepend),实际较少用 |
order=5 |
th:value |
设置 value 值(同类有 th:src、th:href) |
order=6 |
th:text |
设置文本内容,会转义 HTML;th:utext 不转义、原样输出 |
order=7 |
th:fragment |
定义可被 th:insert 引用的代码块 |
order=8(最低) |
注意:th:insert / th:replace / th:include 三者区别较大,用错会破坏 HTML 结构,常用于公共头部、底部、侧边栏的抽取。
两个必须记住的坑
-
只能用
@Controller,不能用@RestController
@RestController相当于@Controller + @ResponseBody,会把返回值直接当字符串写回浏览器,页面不会渲染,只会看到logList这几个字母。 -
return写的是相对路径
配置了prefix和suffix之后,模板放在classpath:/templates/logList.html,Controller 里直接return "logList"即可,不要写完整路径和.html。
实际开发中是否常用
| 场景 | 常用程度 | 说明 |
|---|---|---|
| 前后端分离项目 | 很少 | 前端独立部署,后端只提供 JSON 接口,页面交给 Vue/React |
| 后台管理系统、内部工具 | 常用 | 服务端渲染省事,一个 jar 部署完成 |
| 传统单体 Web 项目 | 常用 | 替代 JSP 的首选方案 |
| 邮件模板、页面片段复用 | 常用 | 用 Thymeleaf 渲染邮件正文、公共片段 |
| 高并发 C 端页面 | 较少 | 多用前端框架 + CDN/Nginx |
结论:
- Spring Boot 里做服务端渲染,Thymeleaf 是首选,官方支持最好,模板即合法 HTML。
- 前后端分离是主流,纯后端项目里 Thymeleaf 更多出现在后台管理、内部系统、邮件模板等场景。
- JSP 在 Spring Boot 中属于「不推荐」选项,新项目不要用。
行者常至,为者常成!