一、前言

  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. Confluence 6 数据中心的缓存

    在 Confluence 数据中心(集群)你需要分布缓存和每一个节点的缓存.在集群管理界面,将会定义分布缓存和节点本地缓存. 缓存配置文件存储在集群共享目录中的 home 目录下面. https:// ...

  2. Confluence 6 修改默认空间标识图片

    空间标识图片在边栏上的站点目录(Sites Directory)中作为图标进行显示.默认的空间标识图片将会应用到所有的空间中,如果你没有自定义的空间标识被定义的话,请查看 Configure the ...

  3. Pod 找不到头文件 解决方法

    在 BuildSetting 中 搜索 User Header Search Paths 然后在下面 User Header Search Paths 中添加 ${SRCROOT}  再将后面参数改为 ...

  4. Vuejs的一些总结

    http://blog.csdn.net/xllily_11/article/details/52312044 原文链接:http://mrzhang123.github.io/2016/07/14/ ...

  5. http之cdn介绍

    百度百科:CDN的全称是Content Delivery Network,即内容分发网络.CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡.内容分发.调度等功能 ...

  6. AppServ安装到一半卡住的问题

    今天在笔记本安装AppServ的时候,运行到Installing mysql service时就卡住不动了,因为之前在自己的台式电脑安装过AppServ,当时是一步成功的,所以觉得这个问题莫名其妙,因 ...

  7. Fiddler抓包1-抓firefox上https请求

    前言 fiddler是一个很好的抓包工具,默认是抓http请求的,对于pc上的https请求,会提示网页不安全,这时候需要在浏览器上安装证书. 一.网页不安全 1.用fiddler抓包时候,打开百度网 ...

  8. AI-跨域、垃圾回收、content_type组见、接口处理

    AI-跨域.垃圾回收.content_type组见.接口处理 跨域 为什么有跨域?什么时候遇见的?答:由于浏览器的同源策略 阻止ajax请求 不阻止src请求:在测试时,项目上线后不会遇见跨域.源:协 ...

  9. java反射机制实例

    1.通过数据库字段反射给对象实例赋值 convertClass.java /** * 通过反射构造对象 * @param obj * @param rs * @return */ public Obj ...

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

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