spring.resources
@EnableWebMvc
Normally you would add @EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath.
This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.
https://spring.io/guides/gs/uploading-files/
# SPRING RESOURCES HANDLING (ResourceProperties)
spring.resources.add-mappings=true # Enable default resource handling.
spring.resources.cache-period= # Cache period for the resources served by the resource handler, in seconds.
spring.resources.chain.cache=true # Enable caching in the Resource chain.
spring.resources.chain.enabled= # Enable the Spring Resource Handling chain. Disabled by default unless at least one strategy has been enabled.
spring.resources.chain.gzipped=false # Enable resolution of already gzipped resources.
spring.resources.chain.html-application-cache=false # Enable HTML5 application cache manifest rewriting.
spring.resources.chain.strategy.content.enabled=false # Enable the content Version Strategy.
spring.resources.chain.strategy.content.paths=/** # Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.fixed.enabled=false # Enable the fixed Version Strategy.
spring.resources.chain.strategy.fixed.paths=/** # Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.fixed.version= # Version string to use for the Version Strategy.
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/ # Locations of static resources.
http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/
1. 背景
前一段时间,WebIDE 开源的过程中,无意间接触到 webjars,觉得比较有趣,于是研究并整理了一下。
webjars 是将前端的库(比如 jQuery)打包成 Jar 文件,然后使用基于 JVM 的包管理器(比如 Maven、Gradle 等)管理前端依赖的方案。
webjars 的效果非常神奇。对于其用法,我们可以在 maven 项目中添加下面的依赖:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.0</version>
</dependency>
然后通过请求 http://localhost:8080/webjars/jquery/3.1.0/jquery.js
即可正确访问到 jquery 文件。
可以再举一个应用场景的例子,比如项目要添加 Api 文档,决定使用 Swagger,demo 参见。效果如图:
该框架有两部分,一部分是 springfox-swagger2 提供后端实现,另一部分是 springfox-swagger-ui 提供前端实现。引入后端实现很简单,加入 maven 依赖即可,但是引入 springfox-swagger-ui 麻烦一些。
一种方式是将该项目编译后的 source 加入到项目。这种方式虽然能达到效果,但版本的升级就成了问题,需要手工维护。
另一种方式就是我们提到的 webjars 了。去 webjars 官网、maven 仓库、官方文档 都可以查到 swagger-ui 依赖。将依赖加入 pom.xml 后,不需要对前端进行任何配置、修改即可引入前端代码。代码的更新也很方便,修改依赖版本号即可。
经过研究才发现,webjars 这并非新的技术,而是利用现有的框架对静态资源的处理方案实现的。接下来我们一起看看 webjars 的实现以及静态资源处理的设计方案。
2. 预备知识
2.1 Servlet 3
我们可以先来看一下 jquery webjar 的包结构:
jquery-3.1.0.jar
└─ META-INF
└─ resources
└─ webjars
└─ jquery
└─ 3.1.0
└─ jquery.js
拿 Servlet 3 举例,应用打成 war 后,Jar(包括 WebJars)会被放在 WEB-INF/lib
目录下,而 Servlet 3 允许直接访问 WEB-INF/lib
下 jar 中的 /META-INF/resources
目录下的资源。简单来说就是 WEB-INF/lib/{\*.jar}/META-INF/resources
下的资源可以被直接访问。
所以对于 Servlet 3,直接使用 http://localhost:8080/webjars/jquery/3.1.0/jquery.js
即可访问到 webjar 中的 jquery.js,而不用做其它的配置。
那么如何在 Spring MVC 中访问 webjars 呢?或者说,Spring MVC 如何处理静态资源?
2.2 Spring MVC
Spring MVC 的入口是 DispatcherServlet,所有的请求都会汇集于该类,而后分发给不同的处理类。如果不做额外的配置,是无法访问静态资源的。
如果想让 Dispatcher Servlet 直接可以访问到静态资源,最简单的方法当然是交给默认的 Servlet。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
这种情况下 Spring MVC 对资源的处理与 Servlet 方式相同。
3. 基础
我们可以通过很简单的配置使得 Spring MVC 有能力处理对静态资源进行处理。
在 Spring MVC 中,资源的查找、处理使用的是责任链设计模式(Filter Chain):
其思路为如果当前 resolver 找不到资源,则转交给下一个 resolver 处理。 当前 resolver 找到资源则立即返回给上级 resovler(如果存在),此时上级 resolver 又可以选择对资源进一步处理或再次返回给它的上级(如果存在)。
配置方法为重写 WebMvcConfigurerAdapter 类的 addResourceHandlers。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/webjars/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
通过这样的配置,就成功添加了一个 PathResourceResolver
。
该 resolver 的作用是将 url 为 /webjars/**
的请求映射到 classpath:/META-INF/resources/webjars/
。
比如请求 http://localhost:8080/webjars/jquery/3.1.0/jquery.js
时, Spring MVC 会查找路径为 classpath:/META-INF/resources/webjars/jquery/3.1.0/jquery.js
的资源文件。
4. 进阶
4.1 为静态资源添加版本号
为了简单起见,我们假设静态资源存放在 classpath:/static
,且映射的 url 为 /static
。
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 映射 /static 的请求到 classpath 下的 static 目录
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static");
}
}
比如,请求 /static/style.css
, 则会直接查找 classpath:/static/style.css
。
我们刚才说到,这段代码实际上是添加了一个 PathResourceResolver,来完成对资源的查找,那么我们是不是可以继续向 Resolver Chain 添加更多的 Resource Resolver,从而实现对静态资源更多样化的处理呢?
答案是肯定的,接下来,我们添加 VersionResourceResolver。
VersionResourceResolver 可以为资源添加版本号。其所作的工作如下:首先使用下一个 resolver 获取资源,如果找到资源则返回,不做其它处理;如果 下一个 resolver 找不到资源,则尝试去掉 url 中的 version 信息,重新调用下一个 resolver 处理,然后无论下一个 resolver 能否处理,都返回其结果。
版本号的策略有两种,下面分别阐述。
4.1.1 指定版本号
指定固定值作为版本号,比如:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static")
// resourceChain(false) 的作用后面会讲解
.resourceChain(false)
// 添加 VersionResourceResolver,且指定版本号
.addResolver(new VersionResourceResolver()
.addFixedVersionStrategy("1.0.0", "/**"));
}
这样,在请求资源时,加上 /1.0.0
前缀,即 http://localhost:8080/static/1.0.0/style.css
也可正确访问。
VersionResourceResolver 在处理该请求时,首先使用 PathResourceResolver 按照配置的映射关系 "/static/**" => "classpath:/static"
处理,即查找文件 classpath:/static/1.0.0/style.css
。由于该文件不存在,VersionResourceResolver 尝试去掉版本号 1.0.0,然后再次查找 classpath:/static/style.css
,找到文件,直接返回。
4.1.2 使用 MD5 作为版本号
除了指定版本号,也可以使用资源的 MD5 作为其版本号,配置方法为:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.resourceChain(false)
.addResolver(new VersionResourceResolver()
.addContentVersionStrategy("/**"));
}
这样,请求资源时,加上资源的 md5,即 http://localhost:8080/static/style-dfbe630979d120fe54a50593f2621225.css
也可正确访问。
由于使用资源的 MD5 作为版本号,是 VersionResourceResolver 的其中一种策略,因此与指定版本号的处理方式相同,不再阐述。
4.2 gzip 压缩
很多时候,为了降低传输的数据量,可以对资源进行压缩。比如可以将 style.css 压缩成 style.css.gz,但是如何让 Spring MVC 在处理对 style.css 的请求时能正确返回 style.css.gz 呢?
为了解决这个问题,我们可以继续添加一个 Resource Resolver —— GzipResourceResolver。
GzipResourceResolver 用来查找资源的压缩版本,它首先使用下一个 Resource Resolver 查找资源,如果可以找到,则再尝试查找该资源的 gzip 版本。如果存在 gzip 版本则返回 gzip 版本的资源,否则返回非 gzip 版本的资源。
比如对于如下的资源:
static
└─ style.css
└─ style.css.gz (使用 gzip 压缩)
在请求 /static/style.css
时,会先使用 PathResourceResolver 查找 style.css,找到后则再次查找 style.css.gz。这里该文件是存在的,因此会返回 style.css.gz 的内容。
PS: 请求头中的 Content-Encoding 要包含 gzip
配置 GzipResourceResolver 很简单:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.resourceChain(false)
.addResolver(new GzipResourceResolver())
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
}
4.3 chain cache
从上面的情况可以看出,Spring MVC 会对资源进行较多的处理。如果每一次请求都做这些处理,无疑会降低服务器的性能。为了避免这种情况,这时可以添加 CachingResourceResolver 来解决这种问题。
CachingResourceResolver 用于缓存其它 Resource Resolver 查找到的资源。因此 CachingResourceResolver 会被放在最外层。请求先到达 CachingResourceResolver,尝试在缓存中查找,如果找到,则直接返回,如果找不到,则依次调用后面的 resolver,直到有一个 resolver 能够找到资源,CachingResourceResolver 将找到的资源缓存起来,下次请求同样的资源时,就可以从缓存中取了。
可能有人会担心缓存资源会占用太多的内存。但实际上并没有资源内容,仅仅是对资源的路径(或者说资源的抽象)进行了缓存。
开启缓存的方法很简单:
.requestChain(true)
前面的例子中都选择关闭 chain cache,原因是缓存的存在会增加调试的难度。因此开发时可以考虑关闭该功能。
4.4 省略 webjar 版本
AbstractResourceResolver
的子类一共有 5 个,我们已经提到了 4 个。最后一个是 WebJarsResourceResolver。
WebJarsResourceResolver 并不需要手动添加。WebJarsResourceResolver 依赖了 webjars-locator
包,因此当添加了 webjars-locator
依赖时,Spring MVC 会自动添加 WebJarsResourceResolver。
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
<version>0.32</version>
</dependency>
WebJarsResourceResolver 的作用是可以省略 webjar 的版本。比如对于请求 http://localhost:8080/webjars/jquery/3.1.0/jquery.js
省略版本号 3.1.10 直接使用 http://localhost:8080/webjars/jquery/jquery.js
也可访问。
至此所有 Spring MVC 提供的 ResourceResolver 都讲完了。Spring MVC 提供的这 4 个 ResourceResolver 基本够用,如果不能满足业务需求,也可以自定义 ResourceResolver 来满足需求。
4.5 Transformer
实际上,除了 ResourceResolver,Spring MVC 还支持修改资源内容,即 Resource Transformer。
可用的 Resource Transformer 有以下几个:
他们的功能依次为:
- AppCacheManifestTransformer: 帮助处理 HTML5 离线应用的 AppCache 清单内的文件
- CachingResourceTransformer: 缓存其它 transfomer 的结果,作用同
CachingResourceResolver
- CssLinkResourceTransformer: 处理 css 文件中的链接,为其加上版本号
- ResourceTransformerSupport: 抽象类,自定义 transfomer 时继承
我们拿 CssLinkResourceTransformer 举例。 它会将 css 文件中的 @import 或 url() 函数中的资源路径自动转换为包含版本号的路径。
配置方法为:
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.resourceChain(false)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
.addTransformer(new CssLinkResourceTransformer());
当我们在 style.css 中通过 @import "style-other.css";
导入了另一个 css 文件,则 transformer 会自动将该 style.css 内部的 css 文件路径地址转换为: @import "style-other-d41d8cd98f00b204e9800998ecf8427e.css"
4.6 Http 缓存
为了避免客户端重复获取资源,HTTP/1.1
规范中定义了 Cache-Control 头。几乎所有浏览器都实现了支持 Cache-Control
。
配置方法如下:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl
.maxAge(10, TimeUnit.MINUTES)
.cachePrivate());
}
当请求 /static/style.css
时,返回的头信息中会多两条信息:
Cache-Control:max-age=600, private
Last-Modified:Sun, 04 Oct 2016 15:08:22 GMT
浏览器会将该信息连同资源储存起来,当再次请求该资源时,会取出 Last-Modified 并添加到在请求头 If-Modified-Since 中:
If-Modified-Since:Sun, 04 Oct 2016 15:08:22 GMT
Spring MVC 在收到请求,发现存在 If-Modified-Since
,会提取出来该值,并与资源的修改时间比较,如果发现没有改变,则仅仅返回状态码 304,无需传递资源内容。浏览器收到状态码 304,明白资源从上次请求到现在未被改变,http 缓存依旧可用。
http 缓存的更多用法参见 这里。
5. 使用 Spring Boot 配置
众所周知,使用 Spring MVC 搭建 Web 服务,不仅要编写不少的代码或 XML 配置,如果开发人员使用不同的 IDE,还要配置这些 IDE 使其得以被正确运行。
为了解决这些问题,spring.io 平台提供了 Spring Boot。Spring Boot 采用 约定优于配置 的理念,在整合已有的 Spring 组件的同时,提供了大量的默认配置。得益于这些默认配置,使用 Spring Boot,只需要编写一个 pom.xml,再加上一个 java 类,就可以跑起来一个 web 服务,如果使用 groovy,一个类文件就能跑起来 web 服务。正是由于 spring boot 带来的这种便捷的特性,被广泛应用在微服务的场景中。
现在,Spring Boot 已经非常成熟了,最好的教程当然是官方文档。
项目的创建可以为普通 maven 项目,当然还可以使用 spring.io 提供的 在线创建 Spring Boot 项目 的服务创建简项目或者。当然,也可以查看本文的示例代码。
强烈推荐 看下 WebMvcAutoConfiguration 这个类,它为 Spring Boot 提供了大量的 Web 服务的默认配置。这些配置包括但不局限于:设置了主页、webjars配置、静态资源位置等。这些配置对于我们使用配置 Web 服务很有借鉴意义。
ps: 想要使用默认配置,无需使用 @EnaleWebMvc 注解。使用了 @EnableWebMvc 注解后 WebMvcAutoConfiguration 提供的默认配置会失效,必须提供全部配置。
最后,我们使用 spring boot 提供的编写配置文件的方式,实现上面使用代码才能完成的功能。
# application.properties
# 设置静态资源的存放地址
spring.resources.static-locations=classpath:/resources
# 开启 chain cache
spring.resources.chain.cache=true
# 开启 gzip
spring.resources.chain.gzipped=true
# 指定版本号
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/static
spring.resources.chain.strategy.fixed.version=1.0.0
# 使用 MD5 作为版本号
spring.resources.chain.strategy.content.enable=true
spring.resources.chain.strategy.content.paths=/**
# http 缓存过期时间
spring.resources.cachePeriod=60
最后介绍一下如何查看这些配置的技巧:
通过查看 ResourceProperties 这个类可以看到,该类顶部有一个注解 @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
。
ConfigurationProperties 是用来注入值的,prefix = "spring.resources"
表示前缀。比如我们配置文件中的 spring.resources.static-locations=classpath:/resources
这个配置,去掉 spring.resources
这个前缀,剩下的为 static-locations
,则它的值 classpath:/resources
会被注入到 ConfigurationProperties 类的 staticLocations 成员变量中。通过这种方法,我们就能通过编写配置文件改变类的状态而无需编写代码。当然,如何使用这些配置的关键还是要知道这些成员变量的作用。
6. 总结
本文从一个新的技术点 webjars 出发,探讨了 Spring MVC 对静态资源的处理,紧接着又了解了 Spring Boot 的配置技巧。
示例代码:下载
7. 参考
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn#cache-control
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-config-static-resources
http://qiita.com/kazuki43zoo/items/e12a72d4ac4de418ee37
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content
https://blog.coding.net/blog/spring-static-resource-process
spring.resources的更多相关文章
- spring boot 启动报错(spring-boot-devtools热部署后):The elements [spring.resources.cache-period] were left unbound. Update your application's configuration
详细错误代码: *************************** APPLICATION FAILED TO START *************************** Descript ...
- spring boot 使用spring.resources.static-locations 分离系统模版&&资源文件
方便我们将资源配置以及模版&&静态文件分离出来,而不是打包在一起,比如以下的一个demo 参考配置: server.port=8006 spring.application.name= ...
- Spring Resources之介绍和资源接口
1.介绍 不幸的是Java的标准的java.net.URL类和针对不同的URL前缀的标准处理器都不够充分去访问所有的低级资源.例如,美誉标准化的URL实现可能用于去范围需要从classpath中或者相 ...
- [ Learning ] Spring Resources
1. Spring MVC Spring MVC原理及配置详解 springMVC系列之(三) spring+springMVC集成(annotation方式) Mybatis3+Spring4+Sp ...
- Spring中无法访问resources目录下页面或静态资源
1.新建项目,在 resources 目录下创建 views 目录,在 views 目录下创建 index.html 页面,项目跑起来,浏览器访问页面,提示找不到页面之类的错误提示. 2.再尝试访问图 ...
- Spring学习八----------Bean的配置之Resources
© 版权声明:本文为博主原创文章,转载请注明出处 Resources 针对于资源文件的统一接口 -UrlResource:URL对应的资源,根据一个URL地址即可创建 -ClassPathResour ...
- 第一个Spring Boot Web程序
需要的环境和工具: 1.Eclipse2.Java环境(JDK 1.7或以上版本)3.Maven 3.0+(Eclipse已经内置了) 写个Hello Spring: 1.新建一个Maven项目,项目 ...
- Spring boot配置文件 application.properties
http://www.tuicool.com/articles/veUjQba 本文记录Spring Boot application.propertis配置文件的相关通用属性 # ========= ...
- Spring Boot 静态资源处理
spring Boot 默认的处理方式就已经足够了,默认情况下Spring Boot 使用WebMvcAutoConfiguration中配置的各种属性. 建议使用Spring Boot 默认处理方式 ...
随机推荐
- 一个类搞定UIScrollView那些事儿
前言 UIScrollView可以说是我们在日常编程中使用频率最多.扩展性最好的一个类,根据不同的需求和设计,我们都能玩出花来,当然有一些需求是大部分应用通用的,今天就聊一下以下需求,在一个categ ...
- 阿里云 云服务器 CentOS 5.8 安装 php 5.4
1.安装php http://webtatic.com/packages/php54/ 2.安装mysql http://webtatic.com/packages/mysql55/ 3.修改mysq ...
- 【Matlab编程】Matlab高效编程技巧
1.默认状态下,matlab显示精度是short型,而默认的计算精度是double型,并且显示精度与计算精度没有关系. 2. 一只失明的猫的问题:注意方法! 3.给数组预分配空间是基本的高效编程准则之 ...
- Media Player Classic - HC 源代码分析 2:核心类 (CMainFrame)(1)
===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...
- 【Android 应用开发】Android游戏音效实现
1. 游戏音效SoundPool 游戏中会根据不同的动作 , 产生各种音效 , 这些音效的特点是短暂(叫声,爆炸声可能持续不到一秒) , 重复(一个文件不断重复播放) , 并且同时播放(比如打怪时怪的 ...
- leetcode之旅(8)-Contains Duplicate
题目: Given an array of integers, find if the array contains any duplicates. Your function should retu ...
- 遗传算法解决TSP问题实现以及与最小生成树的对比
摘要: 本实验采用遗传算法实现了旅行商问题的模拟求解,并在同等规模问题上用最小生成树算法做了一定的对比工作.遗传算法在计算时间和占用内存上,都远远优于最小生成树算法. 程序采用Microsoft vi ...
- 关于JQuery Class选择器的一点
当某个元素的Class为为两个字符串的时候,那用class选择器的时候就必须把两个字符串都写上否则无效 <div class="cla clb">11111<di ...
- Getting Real内容浓缩
今天看完,想整理一下,可能会更好,也给别人提供一个快速学习的途径第一章 什么是 Getting Real?表达形式省略.精炼.精益.敏捷.用户体验.迭代改进.产品简化.第二章 建构从简做得比竟争对手少 ...
- Ocelot中文文档-GraphQL
好吧!你明白我的意思Ocelot并不直接支持GraphQL,但有这么多人问起它,我想表明整合graphql-dotnet库是多么容易 请参阅示例项目OcelotGraphQL. 结合使用graphql ...