【WEB 系列】静态资源配置与读取
SpringWeb 项目除了我们常见的返回 json 串之外,还可以直接返回静态资源(当然在现如今前后端分离比较普遍的情况下,不太常见了),一些简单的 web 项目中,前后端可能就一个人包圆了,前端页面,js/css 文件也都直接放在 Spring 项目中,那么你知道这些静态资源文件放哪里么
<!– more –>
I. 默认配置
1. 配置
静态资源路径,SpringBoot 默认从属性 spring.resources.static-locations
中获取
默认值可以从 org.springframework.boot.autoconfigure.web.ResourceProperties#CLASSPATH_RESOURCE_LOCATIONS
获取
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].
*/
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
注意上面的默认值,默认有四个,优先级从高到低
/META-INF/resources/
/resources/
/static/
/public/
2. 实例演示
默认静态资源路径有四个,所以我们设计 case 需要依次访问这四个路径中的静态资源,看是否正常访问到;其次就是需要判定优先级的问题,是否和上面说的一致
首先创建一个 SpringBoot web 项目,工程创建流程不额外多说,pom 中主要确保有下面依赖即可(本文使用版本为: 2.2.1.RELEASE
)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
在资源文件夹 resources
下,新建四个目录,并添加 html 文件,用于测试是否可以访问到对应的资源文件(主要关注下图中标红的几个文件)
a. META-INF/resources
静态文件 m.html
<html>
<title>META-INF/resource/m.html</title>
<body>
jar 包内,META-INF/resources 目录下 m.html
</body>
</html>
完成对应的 Rest 接口
@GetMapping(path = "m")
public String m() {return "m.html";}
b. resources
静态文件 r.html
<html>
<title>resources/r.html</title>
<body>
jar 包内,resouces 目录下 r.html
</body>
</html>
对应的 Rest 接口
@GetMapping(path = "r")
public String r() {return "r.html";}
c. static
静态文件 s.html
<html>
<title>static/s.html</title>
<body>
jar 包内,static 目录下 s.html
</body>
</html>
对应的 Rest 接口
@GetMapping(path = "s")
public String s() {return "s.html";}
d. public
静态文件 p.html
<html>
<title>public/p.html</title>
<body>
jar 包内,public 目录下 p.html
</body>
</html>
对应的 Rest 接口
@GetMapping(path = "p")
public String p() {return "p.html";}
e. 优先级测试
关于优先级的测试用例,主要思路就是在上面四个不同的文件夹下面放相同文件名的静态资源,然后根据访问时具体的返回来确定相应的优先级。相关代码可以在文末的源码中获取,这里就不赘述了
II. 自定义资源路径
一般来讲,我们的静态资源放在上面的四个默认文件夹下面已经足够,但总会有特殊情况,如果资源文件放在其他的目录下,应该怎么办?
1. 修改配置文件
第一种方式比较简单和实用,修改上面的 spring.resources.static-locations
配置,添加上自定义的资源目录,如在 application.yml
中,指定配置
spring:
resources:
static-locations: classpath:/out/,classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
上面指定了可以扫描 /out
目录下的静态资源文件,且它的优先级是最高的(上面的配置顺序中,优先级的高低从左到右)
实例演示
在资源目录下,新建文件/out/index.html
请注意在其他的四个资源目录下,也都存在 index.html
这个文件(根据上面优先级的描述,返回的应该是/out/index.html
)
@GetMapping(path = "index")
public String index() {return "index.html";}
2. WebMvcConfigurer 添加资源映射
除了上述的配置指定之外,还有一种常见的使用姿势就是利用配置类 WebMvcConfigurer
来手动添加资源映射关系,为了简单起见,我们直接让启动类实现 WebMvcConfigure
接口
@SpringBootApplication
public class Application implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 请注意下面这个映射,将资源路径 /ts 下的资源,映射到根目录为 /ts 的访问路径下
// 如 ts 下的 ts.html, 对应的访问路径 /ts/ts
registry.addResourceHandler("/ts/**").addResourceLocations("classpath:/ts/");
}
public static void main(String[] args) {SpringApplication.run(Application.class);
}
}
根据上面的配置表示将 /ts 目录下的资源 ts.html,映射到 /ts/ts,而直接访问 /ts 会报 404(这个逻辑可能有点绕,需要仔细想一想)
@GetMapping(path = "ts")
public String ts() {return "ts.html";}
@GetMapping(path = "ts/ts")
public String ts2() {return "ts.html";}
III. Jar 包资源访问
前面描述的静态资源访问主要都是当前包内的资源访问,如果我们的静态资源是由第三方的 jar 包提供(比如大名鼎鼎的 Swagger UI),这种时候使用姿势是否有不一样的呢?
1. classpath 与 classpath*
在之前使用 SpringMVC3+/4
的时候,classpath:/META-INF/resources/
表示只扫描当前包内的 /META-INF/resources/
路径,而 classpath*:/META-INF/resources/
则会扫描当前 + 第三方 jar 包中的 /META-INF/resources/
路径
那么在 SpringBoot2.2.1-RELEASE
版本中是否也需要这样做呢?(答案是不需要,且看后面的实例)
2. 实例
新建一个工程,只提供基本的 html 静态资源,项目基本结构如下(具体的 html 内容就不粘贴了,墙裂建议有兴趣的小伙伴直接看源码,阅读效果更优雅)
接着在我们上面常见的工程中,添加依赖
<dependency>
<groupId>com.git.hui.boot</groupId>
<artifactId>204-web-static-resources-ui</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
添加对应资源的访问端点
@GetMapping(path = "ui")
public String ui() {return "ui.html";}
@GetMapping(path = "out")
public String out() {return "out.html";}
// 第三方 jar 包的 META-INF/resources 优先级也高于当前包的 /static
@GetMapping(path = "s2")
public String s2() {return "s2.html";}
请注意,这个时候我们是没有修改前面的 spring.resources.static-locations
配置的
上面的访问结果,除了说明访问第三方 jar 包中的静态资源与当前包的静态资源配置没有什么区别之外,还可以得出一点
- 相同资源路径下,当前包的资源优先级高于 jar 包中的静态资源
- 默认配置下,第三方 jar 包中
META-INF/resources
下的静态资源,优先级高于当前包的/resources
,/static
,/public
IV. 其他
0. 项目
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 源码:https://github.com/liuyueyi/spring-boot-demo/spring-boot/204-web-static-resources
- https://github.com/liuyueyi/spring-boot-demo/spring-boot/204-web-static-resources-ui
1. 一灰灰 Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰 Blog 个人博客 https://blog.hhui.top
- 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top