一、前言

  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 静态资源访问原理解析的更多相关文章

  1. Spring Boot静态资源处理

    Spring Boot静态资源处理 8.8 Spring Boot静态资源处理 当使用Spring Boot来开发一个完整的系统时,我们往往需要用到前端页面,这就不可或缺地需要访问到静态资源,比如图片 ...

  2. 从零开始的Spring Boot(3、Spring Boot静态资源和文件上传)

    Spring Boot静态资源和文件上传 写在前面 从零开始的Spring Boot(2.在Spring Boot中整合Servlet.Filter.Listener的方式) https://www. ...

  3. Spring Boot 静态资源处理

    spring Boot 默认的处理方式就已经足够了,默认情况下Spring Boot 使用WebMvcAutoConfiguration中配置的各种属性. 建议使用Spring Boot 默认处理方式 ...

  4. Spring Boot 静态资源处理(转)

    Spring Boot 静态资源处理 Spring Boot 系列 Spring Boot 入门 Spring Boot 属性配置和使用 Spring Boot 集成MyBatis Spring Bo ...

  5. Spring Boot 静态资源处理,妙!

    作者:liuxiaopeng https://www.cnblogs.com/paddix/p/8301331.html 做web开发的时候,我们往往会有很多静态资源,如html.图片.css等.那如 ...

  6. Spring Boot静态资源

    1.4 SpringBoot静态资源 1.4.1 默认静态资源映射 Spring Boot 对静态资源映射提供了默认配置 Spring Boot 默认将 /** 所有访问映射到以下目录: classp ...

  7. 7.Spring MVC静态资源访问

    在SpringMVC中常用的就是Controller与View.但是我们常常会需要访问静态资源,如html,js,css,image等. 默认的访问的URL都会被DispatcherServlet所拦 ...

  8. 十二、 Spring Boot 静态资源处理

    spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性. 建议大家使用Spring Boot的默认配置方式,如果需要特殊处理的再通 ...

  9. spring boot(二):启动原理解析

    我们开发任何一个Spring Boot项目,都会用到如下的启动类 @SpringBootApplication public class Application { public static voi ...

随机推荐

  1. spark中RDD的转化操作和行动操作

    本文主要是讲解spark里RDD的基础操作.RDD是spark特有的数据模型,谈到RDD就会提到什么弹性分布式数据集,什么有向无环图,本文暂时不去展开这些高深概念,在阅读本文时候,大家可以就把RDD当 ...

  2. Kali linux Nessus &Cracking Password

    1 .Nessus漏洞网站测试(真正体会到什么是专业版和社区版的区别,要技术就不光要勤恳的态度,严谨的思维.还有矢志不渝的志气,还必须要求砸钱,所以狠狠的赚钱才是硬道理),我的半个社区版的很多扫描模块 ...

  3. Win#password;;processon #clone;;disassemble;;source find

    1.密码学思维导图 源地址:https://www.processon.com/view/5a61d825e4b0c090524f5b8b 在这之前给大家分享 如何在 processon上搜索公开克隆 ...

  4. Centos7上实现不同网段的服务器文件共享

    目的:实现不同网段的服务器实现文件共享 前提:服务器1可以和共享服务器互通,共享服务器和服务器2互通 拓扑如下: 思路: 一般文件共享有涉及windown系统的用samba,纯类centos系统就用n ...

  5. 集腋成裘-02-css基础-01

    CSS 层叠样式表(Cascading Style Sheets)(级联样式表) 1 选择器 1.1 写法 选择器是一个选择标签的过程. 选择器{属性:值;...} 1.2 基础选择器 标签选择器 类 ...

  6. 使用CSS选择器定位页面元素

    摘录:http://blog.csdn.net/defectfinder/article/details/51734690 CSS选择器也是一个非常好用的定位元素的方法,甚至比Xpath强大.在自动化 ...

  7. Just oj 2018 C语言程序设计竞赛(高级组)F:Star(结构体排序+最小生成树)

    F: Star Time Limit: 1 s      Memory Limit: 128 MB Submit My Status Problem Description 31世纪,人类世界的科技已 ...

  8. python--使用递归的方式建立二叉树

    树和图的数据结构,就很有意思啦. # coding = utf-8 class BinaryTree: def __init__(self, root_obj): self.key = root_ob ...

  9. [转]Tor Browser在国内Windows平台下的超详细教程

    https://cloudfra.com/tor-browser-windows.html 下载与安装 首先,你必须身处科学式网络(实在怕网站再出问题),接着就可以点击打开Tor Browser官网, ...

  10. JS如何监听动画结束

    场景描述 在使用JS控制动画时一般需要在动画结束后执行回调去进行DOM的相关操作,所以需要监听动画结束进行回调.JS提供了以下事件用于监听动画的结束,简单总结学习下. CSS3动画监听事件 trans ...