JHHK

欢迎来到我的个人网站
行者常至 为者常成

bug菌_web页面

目录

静态资源映射

一句话理解静态资源映射

静态资源映射,就是给 URL 路径和文件路径建立一条对应关系。

浏览器请求 http://localhost:8080/1.jpg,框架按约定去某个目录下找 1.jpg,找到就返回文件,找不到就 404。
这条“URL → 文件地址”的规则就是静态资源映射。它不参与业务逻辑,只负责让图片、JS、CSS、HTML 这些不需要经过 Java 代码处理的文件能被浏览器访问到。

区别于接口:/user/1 走 Controller,返回 JSON;/1.jpg 不经过 Controller,直接由框架把文件流丢回去。

它解决了什么问题

  1. jar 包部署没有 webapp 目录
    传统 war 项目把前端资源放在 webapp 下,容器会自动提供访问。Spring Boot 打成 jar 运行,没有这个概念,必须有新机制告诉框架资源在哪——这就是 WebJars 和默认资源目录存在的原因。

  2. 不再手写 XML 配置
    Spring MVC 时代要写 <mvc:resources mapping="/static/**" location="/static/"/>,漏配就 404。Spring Boot 通过自动配置把默认规则注册好了,开箱即用。

  3. 前端依赖也能用 Maven 管版本
    WebJars 把 jQuery、Bootstrap 打成 jar,版本跟后端依赖一起管理,不用手工下载 JS 文件往项目里塞。

  4. 一个 jar 就能跑起带页面的应用
    小型项目、后台管理页、内网工具,不需要额外搭 Nginx,页面放 static 目录跟着 jar 一起发布。

  5. 外部文件也能通过 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 属于“了解即可”的知识点,面试可能问,实际新项目很少用。

什么时候需要自己配

  1. 默认四个目录不够用:用 spring.web.resources.static-locations 指定新目录,注意这是覆盖默认值,不是追加。
  2. 访问磁盘上的文件:实现 WebMvcConfigurer#addResourceHandlers,把 /img/** 映射到 file:/data/images/。
  3. 需要缓存或权限控制:通过 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 上现成的文件;模板引擎返回的是运行时动态拼接出来的页面。

它解决了什么问题

  1. 接口返回 JSON,不返回页面
    前后端不分离的老式项目中,浏览器要的是 HTML。@RestController 只会把返回值写成 JSON 字符串,页面根本渲染不出来,需要模板引擎承接「数据 → HTML」。

  2. JSP 的局限
    JSP 依赖 Servlet 容器、打成 jar 跑起来麻烦、页面里混入大量 <% %> 标签不美观。Thymeleaf 是纯 Java 类库,模板本身就是合法的 HTML,可以直接用浏览器打开预览。

  3. 前端与后端数据解耦
    模板引擎把「显示(UI)」和「内容(业务数据)」分开,改样式不用动 Java 代码,改数据逻辑不用动页面,美工给的页面拿来就能套。

  4. 一个 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 结构,常用于公共头部、底部、侧边栏的抽取。

两个必须记住的坑

  1. 只能用 @Controller,不能用 @RestController
    @RestController 相当于 @Controller + @ResponseBody,会把返回值直接当字符串写回浏览器,页面不会渲染,只会看到 logList 这几个字母。

  2. 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 中属于「不推荐」选项,新项目不要用。

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.