Spring Boot 静态资源访问原理解析
一、前言
springboot配置静态资源方式是多种多样,接下来我会介绍其中几种方式,并解析一下其中的原理。
二、使用properties属性进行配置
应该说 spring.mvc.static-path-pattern 和 spring.resources.static-locations这两属性是成对使用的,如果不明白其中的原理,总会出现资源404的情况。首先收一下spring.mvc.static-path-pattern代表的是一个Ant Path路径,例如resources/**,表示当你的路径中存在resources/**的时候才会处理请求。比如我们访问“http://localhost:8080/resources/xxx.js”时,很显然,springboot逻辑中会根据模式匹配对url进行匹配,匹配命中后,是如何再定位到具体的资源的呢?这时候spring.resources.static-locations的配置就起作用了。
忘记说了,在springboot中spring.mvc.static-path-pattern的默认值是/**,spring.resources.static-locations的默认值是classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources,servlet context:/,springboot中相关的ResourceHttpRequestHandler就会去spring.resources.static-locations配置的所有路径中寻找资源文件。
所以我之前才说spring.mvc.static-path-pattern 和 spring.resources.static-locations这两属性是成对使用的。
三、springboot中默认对静态资源的处理
调试过程中,通过查看 org.springframework.web.servlet.DispatcherServlet中的handlerMappings变量,我们发现有一个很显眼的 resourceHandlerMapping ,这个是springboot为我们提供的一个默认的静态资源handler,通过全文搜索发现出现在org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport这个类中,也就是这个类包含了@EnableWebMvc注解中的大多数功能,更多的扩展功能请参考org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration。
resourceHandlerMapping 的定义如下。
/**
* Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
* resource handlers. To configure resource handling, override
* {@link #addResourceHandlers}.
*/
@Bean
public HandlerMapping resourceHandlerMapping() {
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
this.servletContext, mvcContentNegotiationManager());
addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
if (handlerMapping != null) {
handlerMapping.setPathMatcher(mvcPathMatcher());
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
handlerMapping.setCorsConfigurations(getCorsConfigurations());
}
else {
handlerMapping = new EmptyHandlerMapping();
}
return handlerMapping;
}
请大家先记住ResourceHandlerRegistry这个类。
首先看一下addResourceHandlers(registry);这个方法,父类DelegatingWebMvcConfiguration做了实现,如下。
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
其中WebMvcConfigurerComposite是操作了WebMvcConfigurer类型的对象的集合。在org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration这个springmvc的自动配置类中,有一个WebMvcConfigurer的实现类,如下。
// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not
// on the classpath
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
... @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Integer cachePeriod = this.resourceProperties.getCachePeriod();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(
registry.addResourceHandler("/webjars/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/")
.setCachePeriod(cachePeriod));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(
this.resourceProperties.getStaticLocations())
.setCachePeriod(cachePeriod));
}
} ...
}
上面的addResourceHandlers方法中,增加了默认的mapping pattern = /webjars/** ,默认的resource location是classpath:/META-INF/resources/webjars/。正是这里的配置,我们在集成swagger的时候,就可以正常访问到swagger webjars中的js文件了。其中红色的代码部分就是用户可以自定义的默认静态资源访问方式,并通过ResourceHandlerRegistry对象进行注册。接着看一下mvcProperties和resourceProperties对应的类吧。
@ConfigurationProperties("spring.mvc")
public class WebMvcProperties {
...
/**
* Path pattern used for static resources.
*/
private String staticPathPattern = "/**";
...
}
WebMvcProperties类中的staticPathPattern field 对应了spring.mvc.static-path-pattern这个属性,可以看到默认值是 "/**"。
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware { ..... private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" }; private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" }; private static final String[] RESOURCE_LOCATIONS; static {
RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
+ SERVLET_RESOURCE_LOCATIONS.length];
System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,
SERVLET_RESOURCE_LOCATIONS.length);
System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);
} private String[] staticLocations = RESOURCE_LOCATIONS; ......
}
ResourceProperties中staticLocations field 对应了 spring.resources.static-locations 这个属性。可以看到默认值是classpath:[/META-INF/resources/, /resources/, /static/, /public/], servlet context:/
四、静态资源的Bean配置
在了解了springboot默认资源的配置的原理(即 spring.mvc.static-path-pattern 和 spring.resources.static-locations),我们可以增加一个WebMvcConfigurer类型的bean,来添加静态资源的访问方式,还记得上面说的“请记住ResourceHandlerRegistry这个类“,下面就用到了哦。
@Configuration
public class ResourceWebMvcConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("classpath:/public-resources/")
.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic());
}
}
那么当访问路径中包含"resources/**"的时候,resource handler就会去classpath:/public-resources目录下寻找了。
五、静态资源的查找
参考 org.springframework.web.servlet.resource.ResourceHttpRequestHandler,ResourceHttpRequestHandler中通过org.springframework.web.servlet.resource.PathResourceResolver进行查找。
举个例子,下图是springboot打包之后的目录结构,现在想要通过url访问application.properties文件,springboot默认的静态文件配置可以吗?当然需要用事实来说话了。

我们已经知道,默认的resource locations中有个 servlet-context:/,访问你的url是http://localhost:8080/工程名/application.properties,调试一下PathResourceResolver,结果如下。


发现servlet-context的根路径如上图所示,查看一下这个路径对应的目录,发现什么都没有,所以很显然无法找到我们要找的文件了。毕竟一般使用springboot都是jar项目,servlet-context path下没有用户自定义的资源。
六、其他方式
在Servlet3协议规范中,包含在JAR文件/META-INFO/resources/路径下的资源可以直接访问了。如果将springboot项目打包成war包,可以配置一个默认的servlet。在WebMvcConfigurationSupport中已经定义好了,不过默认是一个EmptyHandlerMapping。
/**
* Return a handler mapping ordered at Integer.MAX_VALUE with a mapped
* default servlet handler. To configure "default" Servlet handling,
* override {@link #configureDefaultServletHandling}.
*/
@Bean
public HandlerMapping defaultServletHandlerMapping() {
DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext);
configureDefaultServletHandling(configurer);
AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping();
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
return handlerMapping;
}
可以通过自定义一个WebMvcConfigurer类型的bean,改写configureDefaultServletHandling 方法,如下。
@Configuration
public class MyWebConfigurer extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
这样就设置了一个默认的servlet,在加载静态资源的时候就会按照servelt方式去加载了。
就先分享这么多了,更多分享请关注我们的技术公众号吧!!!
Spring Boot 静态资源访问原理解析的更多相关文章
- Spring Boot静态资源处理
Spring Boot静态资源处理 8.8 Spring Boot静态资源处理 当使用Spring Boot来开发一个完整的系统时,我们往往需要用到前端页面,这就不可或缺地需要访问到静态资源,比如图片 ...
- 从零开始的Spring Boot(3、Spring Boot静态资源和文件上传)
Spring Boot静态资源和文件上传 写在前面 从零开始的Spring Boot(2.在Spring Boot中整合Servlet.Filter.Listener的方式) https://www. ...
- Spring Boot 静态资源处理
spring Boot 默认的处理方式就已经足够了,默认情况下Spring Boot 使用WebMvcAutoConfiguration中配置的各种属性. 建议使用Spring Boot 默认处理方式 ...
- Spring Boot 静态资源处理(转)
Spring Boot 静态资源处理 Spring Boot 系列 Spring Boot 入门 Spring Boot 属性配置和使用 Spring Boot 集成MyBatis Spring Bo ...
- Spring Boot 静态资源处理,妙!
作者:liuxiaopeng https://www.cnblogs.com/paddix/p/8301331.html 做web开发的时候,我们往往会有很多静态资源,如html.图片.css等.那如 ...
- Spring Boot静态资源
1.4 SpringBoot静态资源 1.4.1 默认静态资源映射 Spring Boot 对静态资源映射提供了默认配置 Spring Boot 默认将 /** 所有访问映射到以下目录: classp ...
- 7.Spring MVC静态资源访问
在SpringMVC中常用的就是Controller与View.但是我们常常会需要访问静态资源,如html,js,css,image等. 默认的访问的URL都会被DispatcherServlet所拦 ...
- 十二、 Spring Boot 静态资源处理
spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性. 建议大家使用Spring Boot的默认配置方式,如果需要特殊处理的再通 ...
- spring boot(二):启动原理解析
我们开发任何一个Spring Boot项目,都会用到如下的启动类 @SpringBootApplication public class Application { public static voi ...
随机推荐
- 第十六单元 yum管理RPM包
yum的功能 本地yum配置 光盘挂载和镜像挂载 本地yum配置 网络yum配置 网络yum配置 Yum命令的使用 使用yum安装软件 使用yum删除软件 安装组件 删除组件 清除缓存 查询 课后作业 ...
- flower
问题 D: flower 时间限制: 1 Sec 内存限制: 128 MB 题目描述 又是一年教师节,有n个小朋友打算去看望花老师.去看花老师当然要买花咯,不过某些土(xue)豪(ba)买了太多的花 ...
- tinymce-vue富文本编辑器(翻译)
官方Tinymce Vue组件 翻译来自:https://github.com/tinymce/tinymce-vue 官方文档:https://www.tiny.cloud/docs/general ...
- 使用Ultra Librarian将bxl文件转为OrCAD Capture CIS可识别的库文件(OLB)
操作系统:Windows 10 x64 工具1:Ultra Librarian 8.3.89 工具2:OrCAD Capture CIS 16.6-S062 (v16-6-112FF) 关于Ultra ...
- eclipse创建动态maven项目
需求表均同springmvc案例 此处只是使用maven 注意,以下所有需要建立在你的eclipse等已经集成配置好了maven了,说白了就是新建项目的时候已经可以找到maven了 没有的话需要安装m ...
- 微信小程序--WXS---JS 代码插入
主页要使用的JS代码放在index.js里面 例 Page({ data: { array: [1, 2, 3, 4, 5, 1, 2, 3, 4] } }) 只复制以下这段 data: { arra ...
- C#递归拷贝文件删除文件
拷贝文件及子文件,最后一个参数排除,哪个不要删除.(其实就是移动的效果) //拷贝文件及子文件 public static void CopyDirectory(string src, string ...
- asp.net core WebApi 返回 HttpResponseMessage
ASP.NET WebApi 2 中的示例代码: [Route("values/{id}")] public async Task<HttpResponseMessage&g ...
- Git reset与checkout的区别
reset: 将暂存区的文件回撤到工作区,文件内容不会有任何变化 checkout: 将工作区文件恢复到上一次commit时的内容,将会丢失修改了但未加入暂存区的内容
- pkusc2018数学题
题解: 还是很水的 复制个题面过来 好吧我不会复制 https://www.cnblogs.com/skylee03/p/9127130.html 题目链接 第一题是个傻逼题 初中数学题随便凑凑 ( ...