文章目录
- [一、背景:为什么 jar 内的 config.json 不能被覆盖?](#一、背景:为什么 jar 内的 config.json 不能被覆盖?)
- [二、Spring Boot 静态资源加载机制](#二、Spring Boot 静态资源加载机制)
- 三、解决方案结构
- 四、核心实现:优先读外部文件
- [五、完整 Dockerfile 示例](#五、完整 Dockerfile 示例)
- 八、总结
前言:在实际项目中,我们经常需要让前端读取一个 config.json,并且希望它 支持在 Docker 容器里挂载覆盖、实时生效 ,避免每次修改都要重新打包。
一、背景:为什么 jar 内的 config.json 不能被覆盖?
很多人遇到这样的场景:
- 前端请求
GET /config.json - 想通过 Docker 挂载一份外部
config.json到容器里覆盖 jar 里的那份 - 结果发现改了也不生效 → 还是读到打包进 jar 的老版本
🔎 根本原因:
Spring Boot 不会自动替换 jar 内部的静态资源文件,挂载的静态资源只是在查找路径里排在前面而已。
换句话说:
文件如果在 jar 里,通过 classpath 加载的优先级并不因为挂载而被覆盖(实际查找路径可能不同)。
二、Spring Boot 静态资源加载机制
在 Spring Boot 里,静态资源是这样被查找的:
properties
# Spring Boot 2.4+ 使用这个
spring.web.resources.static-locations=file:/app/static/, classpath:/static/
# Spring Boot 2.3- 使用这个
spring.resources.static-locations=file:/app/static/, classpath:/static/
这个配置表示:
| 优先级 | 资源位置 |
|---|---|
| 第一 | 外部目录 file:/app/static/ |
| 第二 | jar 内 classpath:/static/ |
注意:这是查找顺序,不是覆盖机制。
三、解决方案结构
为了做到:
✔ Docker 容器挂载的 config.json 可覆盖
✔ 修改后立即生效(无重启,无缓存)
✔ 支持 /config.json 和 /wvp/config.json 两种访问路径
我们的整体方案是:
前端请求 /config.json 或 /wvp/config.json
→ 后端 Controller 先尝试读取 容器内 /app/config/config.json
→ 如果存在直接返回
→ 如果不存在再 fallback 到 classpath:/static/config.json
同时设置返回头 Cache-Control: no-store 避免浏览器缓存旧配置。
四、核心实现:优先读外部文件
在你的 Controller 里写如下逻辑:
java
@RestController
public class WvpIndexController {
private static final String EXTERNAL_PATH = "/app/config/config.json";
private static final String CLASSPATH_PATH = "classpath:/static/config.json";
@GetMapping({"/config.json", "/wvp/config.json"})
public ResponseEntity<Resource> getConfig(HttpServletResponse response) {
Resource resource;
// 1) 优先尝试读取外部挂载文件
File external = new File(EXTERNAL_PATH);
if (external.exists() && external.isFile()) {
resource = new FileSystemResource(external);
} else {
// 2) 不存在则 fallback 到内置 classpath
resource = new ClassPathResource("static/config.json");
}
// 设置不缓存
response.setHeader("Cache-Control", "no-store");
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(resource);
}
}
五、完整 Dockerfile 示例
dockerfile
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY wvp.jar /app/wvp.jar
COPY static /app/static # 静态资源 fallback
EXPOSE 8080
ENTRYPOINT ["java", \
"-Dspring.web.resources.static-locations=file:/app/static/,classpath:/static/", \
"-Dspring.resources.static-locations=file:/app/static/,classpath:/static/", \
"-jar", "wvp.jar"]
八、总结
| 方案 | 是否可挂载覆盖 | 是否热更新 | 是否兼容多个路径 |
|---|---|---|---|
| 只依赖 Spring Boot 静态资源 | ❌ | ❌ | ❌ |
| 加上后端优先逻辑 | ✅ | ✅ | ✅ |
✨ 推荐方案:后端优先读取外部挂载文件 + fallback classpath + no-cache
这样你只要:
bash
docker-compose up -d
然后在宿主机改 config.json,前端刷新立即生效 💪