在 Spring Boot 中,默认已经提供了静态资源处理,我们可以使用 WebMvcConfigurer 进行配置。个人建议大家还是使用 Spring Boot 默认的配置方式比较好,如果有特殊情况,通过配置进行修改即可。
1 静态资源的默认映射
Spring Boot 默认可以直接访问四个目录(目录处于 src/main/resources 资源目录下)下的文件:
- classpath:/META-INF/resources
- classpath:/resources
- classpath:/static
- classpath:/public
目录的先后顺序为静态资源的查找顺序。 若出现了多个同名的静态资源,以前面找到的为准。
需要注意:
- 默认配置的 /** 会映射到 classpath:/META-INF/resources 等四个目录
- 默认配置的 /webjars/** 会映射到 classpath:/META-INF/resources/webjars/
若我们想访问 img.jpg ,请求地址应该为 http://localhost:8080/img.jpg 。
2 静态资源的自定义映射
上面我们示例的资源是打包在 jar 包之中的,但实际上,有部分资源是在需要动态维护的,不可能放在程序包中。对于这种随意指定目录的资源,应该如何访问?
由于需求,现在我们需要增加从 /test/* 到 classpath:/test/* 的映射
正常来说,直接输入地址 http://localhost:8080/test/img.jpg ,根本无法访问
此时,我们需要通过代码来自定义目录映射,只需要实现 WebMvcConfigurer 接口并实现 addResourceHandlers 方法即可。代码如下
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/test/**").addResourceLocations("classpath:/test/");
}
}
此时直接输入地址 http://localhost:8080/test/img.jpg 便可访问
注意,我们的自定义目录映射是不会影响默认映射的,它们可以同时使用。
覆盖自动映射
若我们将 /* 修改为映射到 /test/* ,则会覆盖系统的配置,我们可以多次使用 addResourceLocations 添加目录,优先级先添加的高于后添加的。
registry.addResourceHandler("/**").addResourceLocations("classpath:/test/").addResourceLocations("classpath:/static/");
访问 test 根目录下的 img.jpg 的 URL为 http://localhost:8080/img.jpg (/** 会覆盖系统默认的配置)
外部目录的使用
若我们需要使用绝对路径的文件夹,写法如下即可:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/test/**").addResourceLocations("file:D:/test/");
}
}
注意指定磁盘绝对路径时需要加上 file 。
配置文件配置静态资源的映射
其实我们可以在 application.properties 配置映射,代码如下:
# 默认值为 /**
spring.mvc.static-path-pattern =
# 默认值为 classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
spring.resources.static-locations = 这里设置要指向的路径,多个使用英文逗号隔开,
- spring.mvc.static-path-pattern 可以重新定义 pattern,如修改为 /test/** ,则访问 static 等目录下的 img.jpg 文件应该为 localhost:8080/test/img.jpg ,修改之前为localhost:8080/img.jpg 。注意,spring.mvc.static-path-pattern 只可以定义一个
- 使用 spring.resources.static-locations 可以重新定义 pattern 所指向的路径。
3 WebJars
对于 web 开发而言,对静态资源版本管理是比较混乱的,以 Jquery,Bootstrap举例,可能各个前端框架所依赖的各个组件的版本都不尽相同,一不注意就可能引起不同版本的冲突问题。所以,是否有一种像后端管理 jar 包一样的解决方案呢?答案当然是有的,这就是我们要讲到的 WebJars。
WebJars 的工作原理
WebJars 是将 web 前端资源打成 jar 包文件。借助版本管理工具(例如 Maven )进行版本管理,保证这些 web 资源的版本唯一性,避免文件混乱、版本不一致等问题。
实际上 WebJars 就是将资源文件放到 classpath:/META-INF/resources/webjars/ 中,然后打包成 jar 发布到 maven 仓库中。
WebJars 的应用
在 Spring Boot 中默认将 /webjars/** 映射到 classpath:/META-INF/resources/webjars/ ,因此,在 JSP 页面中引入 jquery.js 的方法为:
<script type="text/javascript" src="${pageContext.request.contextPath }/webjars/jquery/2.1.4/jquery.js"></script>
同时需要在 pom.xml 文件中添加 jquery 的 WebJars 依赖
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>2.1.4</version>
</dependency>
版本号统一管理
在实际开发中,也许我们会遇到升级版本号的情况,若有上千的页面,全部页面都按上面引入了 jquery.js ,那么如果有一天需要把版本号升级为 3.0.0,岂不累死?显然不能一个一个页面地去修改。
下面提供一个进行版本号统一管理的方法
在pom.xml 中添加依赖:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
</dependency>
增加一个WebJarsController:
@Controller
public class WebJarsController {
private final WebJarAssetLocator assetLocator = new WebJarAssetLocator();
@ResponseBody
@RequestMapping("/webjarslocator/{webjar}/**")
public ResponseEntity<Object> locateWebjarAsset(@PathVariable String webjar, HttpServletRequest request) {
try {
String mvcPrefix = "/webjarslocator/" + webjar + "/"; // This prefix must match the mapping path!
String mvcPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String fullPath = assetLocator.getFullPath(webjar, mvcPath.substring(mvcPrefix.length()));
return new ResponseEntity<>(new ClassPathResource(fullPath), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
在页面中使用的方式:
<script type="text/javascript" src="${pageContext.request.contextPath }/webjarslocator/jquery/jquery.js"></script>
4 对静态资源的版本管理
当资源内容发生改变时,由于浏览器缓存,用户本地的资源还是旧资源,为了防止这种情况发生。我们可能会选择在资源文件后面加上参数“版本号”或其他方式。
使用版本号参数的例子:
<script type="text/javascript" src="${pageContext.request.contextPath }/js/common.js?v=1.0.1"></script>
当文件修改后,手工修改版本号来达到 URL 文件不被浏览器缓存的目的。同样也存在很多文件都需要修改的问题。
Spring 为解决这个问题提供了 资源名称md5方式 的方法。
首先我们需要修改 application.properties 配置文件
spring.resources.chain.strategy.content.enabled = true
spring.resources.chain.strategy.content.paths = /**
所有 /** 请求的静态资源都会被处理。
然后创建 ResourceUrlProviderController 文件:
//处理静态资源URL
@ControllerAdvice
public class ResourceUrlProviderController {
@Autowired
private ResourceUrlProvider resourceUrlProvider;
@ModelAttribute("urls")
public ResourceUrlProvider urls() {
return this.resourceUrlProvider;
}
}
那在页面中使用的写法:
<script type="text/javascript" src="${pageContext.request.contextPath }${urls.getForLookupPath('/js/common.js') }"></script>
而当访问页面后,HTML 中实际生成的代码为:
<script type="text/javascript" src="/test/js/common-c6b7da8fffc9be141b48c073e39c7340.js"></script>
注意,在使用 md5 文件名方式的时候,Spring 是有缓存机制的,即在服务不重启的情况下,变动修改这些资源文件,其文件名的 md5 值并不会改变,只有重启服务再次访问才会生效。
5 对 Spring Boot 中静态资源处理的总结
- 对于第三方库,由于在项目开发中变动频率很小,即便是变动也是版本号的修改。 所以我建议使用 WebJars 的方式,并通过动态版本号( webjars-locator 的方式)来使用。
- 对于我们自己存放的静态资源文件(自己写的 js,css 文件等),由于改动频繁,所以我建议使用 md5 资源文件名的方式来使用。
- 对于项目素材文件,建议放到 classpath:/static 目录下。