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 ...
随机推荐
- Confluence 6 临时目录(安装目录)
temp 目录是由 Java 运行时进行配置的,同时一些 Confluence 的组件将会写入历史文件或者锁定文件到这个目录中. 临时目录位于安装目录下的 /temp 目录中. 希望修改这个目录的位置 ...
- django配置一个网站建设
第一步: 安装数据库MySQL,也可以使用pycharm自带的数据库sqllite,大项目要使用数据库.安装请参考上篇. 数据库在pycharm中驱动设置,setting文件中修改驱动文件密码等信息. ...
- JavaScript实现的抛物线运动效果
css88 技术文档地址: http://www.css88.com/archives/5355 张鑫旭 技术文档地址: https://www.zhangxinxu.com 使用示例: 使用时直接引 ...
- spring集成jwt验证方式,token验证
为什么要告别session?有这样一个场景,系统的数据量达到千万级,需要几台服务器部署,当一个用户在其中一台服务器登录后,用session保存其登录信息,其他服务器怎么知道该用户登录了?(单点登录), ...
- shell 脚本加密
日常编写shell脚本时会写一些账号和密码写入脚本内,但是不希望泄露账号密码,所以对shell脚本进行加密变成可执行文件. 主要使用 shc 对 Linux shell 脚本加密,shc是一个专业的加 ...
- Chrome开发者控制台操作教程
1清空控制台 在控制台下有个clear console的按钮,点击的时候会清空控制台. 清空控制台 2让Chrome中的页面可编辑 有的时候我们需要临时改变页面上的文字,图案等信息,一种常见的方法是 ...
- 网络通信socket连接数上限
socket accept()返回的socket描述符的端口和listen描述符端口是一样的吗? as you know,一个socket是由一个五元组来唯一标示的,即(协议,server_ip, s ...
- python---自己来打通节点,链表,栈,应用
但,, 没有调试通过. 思路是对的,立此存照. 关键就是用链表完全实现列表的功能, 替换了就应该OK的. # coding = utf-8 # 节点初始化 class Node: def __init ...
- sdoi2017苹果树
题解: 非常奇妙的一题.. 没有免费操作我都不会$nk$....考试打个暴力就可以走人了 树上有依赖背包问题的正确做法是(为啥我之前学的不是这样的啊) 按照后续遍历做背包 做到一个点的时候 枚举它选不 ...
- 【bzoj3174】[Tjoi2013]拯救小矮人
题解: 这题非常容易想到一个错误的贪心 就是按照ai排序 然后尽量取ai小的 但是有很明显的问题就是如果取了小的ai 那会导致那些原本可能存在这些ai就可以去掉的大的人因此不能取了 而有可能可以先去取 ...